I'm following along in the ZeroMQ book, and trying the Python examples. I'm at lbbroker3.py, and trying to switch it from using multithreading to multiprocessing in Python 3. I was looking at another python ZeroMQ multiprocessing example online from a blog, and tried to incorporate the least amount of changes.
Whenever I run my code, it hangs at joining the first worker thread. The worker thread seems to be hung on "socket.recv_multipart()".
######################################################################HERE IS MY CODE:
import zmqfrom zmq.eventloop.ioloop import IOLoopfrom zmq.eventloop.zmqstream import ZMQStreamfrom multiprocessing import Queue, Process, Eventimport timefrom time import sleepNUM_CLIENTS = 12NUM_WORKERS = 4def client_proc(client_url, ident): # Basic request-reply client using REQ socket context = zmq.Context.instance() socket = context.socket(zmq.REQ) socket.identity = (u"Client-%d" % (ident)).encode('ascii') socket.connect(client_url) # Send request, get reply: socket.send(b"HELLO") reply = socket.recv() print("%s: %s\n" % (socket.identity.decode('ascii'), reply.decode('ascii')), end='')def worker_proc(ident, worker_url): # Before doing work, set up IPC via ZeroMQ # Note: Worker uses local REQ socket to connect to ROUTER socket in broker context = zmq.Context.instance() socket = context.socket(zmq.REQ) socket.identity = (u"Worker-%d" % (ident)).encode('ascii') socket.connect(worker_url) # Tell the broker we are ready for work socket.send(b"READY") try: while True: address, empty, request = socket.recv_multipart() # This line is where # the program hangs upon attempting to join first worker process. print("%s: %s\n" % (socket.identity.decode('ascii'), request.decode('ascii')), end='') socket.send_multipart([address, b'', b'OK']) except zmq.ContextTerminated: # context terminated so quit silently returnclass LRUQueue(object): # LRUQueue class using ZMQStream/IOLoop for event dispatching def __init__(self, backend_socket, frontend_socket): self.available_workers = 0 self.is_workers_ready = False self.workers = [] self.client_nbr = NUM_CLIENTS self.backend = ZMQStream(backend_socket) self.frontend = ZMQStream(frontend_socket) self.backend.on_recv(self.handle_backend) self.loop = IOLoop.instance() def handle_backend(self, msg): # Queue worker address for LRU routing worker_addr, empty, client_addr = msg[:3] assert self.available_workers < NUM_WORKERS # add worker back to the list of workers self.available_workers += 1 self.is_workers_ready = True self.workers.append(worker_addr) # 2nd frame is empty assert empty == b"" # Third frame is READY or else a client reply address # If client reply, send rest back to frontend if client_addr != b"READY": empty, reply = msg[3:] # Following frame is empty assert empty == b"" self.frontend.send_multipart([client_addr, b'', reply]) self.client_nbr -= 1 print(f"num clients: {self.client_nbr}") if self.client_nbr == 0: # Exit after N messages self.loop.add_timeout(time.time() + 1, self.loop.stop) if self.is_workers_ready: # When at least 1 worker ready, start accepting frontend msgs self.frontend.on_recv(self.handle_frontend) def handle_frontend(self, msg): # Now get next client request, route to LRU worker # Client request is [address][empty][request] client_addr, empty, request = msg assert empty == b"" # Dequeue and drop the next worker address self.available_workers -= 1 worker_id = self.workers.pop() self.backend.send_multipart([worker_id, b'', client_addr, b'', request]) if self.available_workers == 0: # stop receiving until workers become available again self.is_workers_ready = False self.frontend.stop_on_recv()def main(): #print("Number of CPU cores: ", multiprocessing.cpu_count()) #num_workers = multiprocessing.cpu_count() - 1 #num_workers = 2 url_worker = "ipc://backend.ipc" url_client = "ipc://frontend.ipc" # Prepare our ZMQ context and sockets context = zmq.Context() frontend = context.socket(zmq.ROUTER) frontend.bind(url_client) backend = context.socket(zmq.ROUTER) backend.bind(url_worker) ## Generate grid of Mandelbrot values #gridvals = generate_mandelgrid(xmin, xmax, ymin, ymax, num_pixels, num_iters, max_val) # spawn worker processes workers = [] clients = [] for i in range(NUM_WORKERS): workers.append( Process(target=worker_proc, args=(i, url_worker)) ) workers[i].start() for i in range(NUM_CLIENTS): clients.append( Process(target=client_proc, args=(url_client, i)) ) clients[i].start() # Create queue with sockets queue = LRUQueue(backend, frontend) # Start reactor # NOTE: starting an IOLoop should be done AFTER all process forking. One IOLoop per process. no sharing. IOLoop.instance().start() print("Program Closing") sleep(1) frontend.close() backend.close() print("sockets closed") sleep(1) context.term() print("context terminated") i = 1 for cli in clients: print(f"Joining Client {i}") cli.join() i += 1 i = 1 for work in workers: print(f"Joining Worker {i}") # Hangs at joining first worker work.join() i += 1 print(f"NEVER REACHED HERE") return 0if __name__ == "__main__": main()
###################################################################################HERE IS THE OUTPUT WHEN I RUN IT:
$ python3 lbbroker3-mp.py/home/rpc/workspace/src/multibrot/lbbroker3-mp.py:2:VisibleDeprecationWarning: zmq.eventloop.minitornado is deprecated inpyzmq 14.0 and will be removed.Install tornado itself to use zmq with the tornado IOLoop.
from zmq.eventloop.ioloop import IOLoop Worker-0: HELLO Worker-2:HELLO num clients: 11 Worker-3: HELLO Worker-1: HELLO num clients: 10Client-0: OK Worker-0: HELLO num clients: 9 Client-1: OK Worker-2:HELLO num clients: 8 Client-2: OK num clients: 7 Worker-3: HELLOClient-3: OK Client-4: OK num clients: 6 num clients: 5 Worker-1:HELLO Worker-0: HELLO Client-5: OK num clients: 4 Worker-3: HELLOWorker-2: HELLO num clients: 3 num clients: 2 Client-6: OK Client-8:OK Client-7: OK num clients: 1 Client-10: OK Client-9: OK Worker-3:HELLO num clients: 0 Client-11: OK Program Closing sockets closedcontext terminated Joining Client 1 Joining Client 2 Joining Client 3Joining Client 4 Joining Client 5 Joining Client 6 Joining Client 7Joining Client 8 Joining Client 9 Joining Client 10 Joining Client 11Joining Client 12 Joining Worker 1
^CProcess Process-2: Process Process-4: Process Process-1: Traceback(most recent call last): File"/home/rpc/workspace/src/multibrot/lbbroker3-mp.py", line 182, inmain() File "/home/rpc/workspace/src/multibrot/lbbroker3-mp.py", line 174, in mainwork.join() File "/usr/lib/python3.10/multiprocessing/process.py", line 149, in joinres = self._popen.wait(timeout) File "/usr/lib/python3.10/multiprocessing/popen_fork.py", line 43, in waitTraceback (most recent call last):return self.poll(os.WNOHANG if timeout == 0.0 else 0) File "/usr/lib/python3.10/multiprocessing/popen_fork.py", line 27, in pollFile "/usr/lib/python3.10/multiprocessing/process.py", line 314, in_bootstrapself.run() File "/usr/lib/python3.10/multiprocessing/process.py", line 108, in runself._target(*self._args, **self._kwargs) File "/home/rpc/workspace/src/multibrot/lbbroker3-mp.py", line 37, inworker_procaddress, empty, request = socket.recv_multipart() # This line is where File "/usr/lib/python3/dist-packages/zmq/sugar/socket.py",line 625, in recv_multipartparts = [self.recv(flags, copy=copy, track=track)] File "zmq/backend/cython/socket.pyx", line 781, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 817, inzmq.backend.cython.socket.Socket.recvpid, sts = os.waitpid(self.pid, flag) File "zmq/backend/cython/socket.pyx", line 186, inzmq.backend.cython.socket._recv_copy File"zmq/backend/cython/checkrc.pxd", line 13, inzmq.backend.cython.checkrc._check_rc KeyboardInterruptKeyboardInterrupt Traceback (most recent call last): Traceback (mostrecent call last): File"/usr/lib/python3.10/multiprocessing/process.py", line 314, in_bootstrapself.run() File "/usr/lib/python3.10/multiprocessing/process.py", line 108, in runself._target(*self._args, **self._kwargs) File "/home/rpc/workspace/src/multibrot/lbbroker3-mp.py", line 37, inworker_procaddress, empty, request = socket.recv_multipart() # This line is where File "/usr/lib/python3/dist-packages/zmq/sugar/socket.py",line 625, in recv_multipartparts = [self.recv(flags, copy=copy, track=track)] File "zmq/backend/cython/socket.pyx", line 781, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 817, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 186, inzmq.backend.cython.socket._recv_copy File"zmq/backend/cython/checkrc.pxd", line 13, inzmq.backend.cython.checkrc._check_rc File"/usr/lib/python3.10/multiprocessing/process.py", line 314, in_bootstrapself.run() File "/usr/lib/python3.10/multiprocessing/process.py", line 108, in runself._target(*self._args, **self._kwargs) File "/home/rpc/workspace/src/multibrot/lbbroker3-mp.py", line 37, inworker_procaddress, empty, request = socket.recv_multipart() # This line is where KeyboardInterrupt File"/usr/lib/python3/dist-packages/zmq/sugar/socket.py", line 625, inrecv_multipartparts = [self.recv(flags, copy=copy, track=track)] File "zmq/backend/cython/socket.pyx", line 781, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 817, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 186, inzmq.backend.cython.socket._recv_copy File"zmq/backend/cython/checkrc.pxd", line 13, inzmq.backend.cython.checkrc._check_rc KeyboardInterrupt ProcessProcess-3: Traceback (most recent call last): File"/usr/lib/python3.10/multiprocessing/process.py", line 314, in_bootstrapself.run() File "/usr/lib/python3.10/multiprocessing/process.py", line 108, in runself._target(*self._args, **self._kwargs) File "/home/rpc/workspace/src/multibrot/lbbroker3-mp.py", line 37, inworker_procaddress, empty, request = socket.recv_multipart() # This line is where File "/usr/lib/python3/dist-packages/zmq/sugar/socket.py",line 625, in recv_multipartparts = [self.recv(flags, copy=copy, track=track)] File "zmq/backend/cython/socket.pyx", line 781, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 817, inzmq.backend.cython.socket.Socket.recv File"zmq/backend/cython/socket.pyx", line 186, inzmq.backend.cython.socket._recv_copy File"zmq/backend/cython/checkrc.pxd", line 13, inzmq.backend.cython.checkrc._check_rc KeyboardInterrupt