if __name__ == '__main__': url_list = ["abc.com", "xyz.com"] task_list = [multiprocessing.Process(target=request_url, args=(url,)) for url in url_list] [task.start() for task in task_list] [task.join() for task in task_list]
if __name__ == '__main__': process_manager = multiprocessing.Manager() result_dict = process_manager.dict() url_list = ["abc.com", "xyz.com"] task_list = [multiprocessing.Process(target=request_url, args=(url, result_dict)) for url in url_list] [task.start() for task in task_list] [task.join() for task in task_list] print(result_dict)
import multiprocessing, Queue import os import time from multiprocessing import Process from time import sleep from random import randint
classProducer(multiprocessing.Process): def__init__(self, queue): multiprocessing.Process.__init__(self) self.queue = queue defrun(self): whileTrue: self.queue.put('one product') print(multiprocessing.current_process().name + str(os.getpid()) + ' produced one product, the no of queue now is: %d' %self.queue.qsize()) sleep(randint(1, 3)) classConsumer(multiprocessing.Process): def__init__(self, queue): multiprocessing.Process.__init__(self) self.queue = queue defrun(self): whileTrue: d = self.queue.get(1) if d != None: print(multiprocessing.current_process().name + str(os.getpid()) + ' consumed %s, the no of queue now is: %d' %(d,self.queue.qsize())) sleep(randint(1, 4)) continue else: break #create queue queue = multiprocessing.Queue(40) if __name__ == "__main__": print('Excited!") #create processes processed = [] for i in range(3): processed.append(Producer(queue)) processed.append(Consumer(queue)) #start processes for i in range(len(processed)): processed[i].start() #join processes for i in range(len(processed)): processed[i].join()
if __name__ == '__main__': with concurrent.futures.ProcessPoolExecutor(max_workers=3) as executor: for url, data inzip(task_url, executor.map(load_url, task_url)): print('%r page is %d bytes' % (url, len(data)))
classProcessPoolExecutor(_base.Executor): def__init__(self, max_workers=None): """Initializes a new ProcessPoolExecutor instance. Args: max_workers: The maximum number of processes that can be used to execute the given calls. If None or not given then as many worker processes will be created as the machine has processors. """ _check_system_limits()
if max_workers isNone: self._max_workers = os.cpu_count() or1 else: if max_workers <= 0: raise ValueError("max_workers must be greater than 0")
self._max_workers = max_workers
# Make the call queue slightly larger than the number of processes to # prevent the worker processes from idling. But don't make it too big # because futures in the call queue cannot be cancelled. self._call_queue = multiprocessing.Queue(self._max_workers + EXTRA_QUEUED_CALLS) # Killed worker processes can produce spurious "broken pipe" # tracebacks in the queue's own worker thread. But we detect killed # processes anyway, so silence the tracebacks. self._call_queue._ignore_epipe = True self._result_queue = SimpleQueue() self._work_ids = queue.Queue() self._queue_management_thread = None # Map of pids to processes self._processes = {}
defsubmit(self, fn, *args, **kwargs): with self._shutdown_lock: if self._broken: raise BrokenProcessPool('A child process terminated ' 'abruptly, the process pool is not usable anymore') if self._shutdown_thread: raise RuntimeError('cannot schedule new futures after shutdown') f = _base.Future() w = _WorkItem(f, fn, args, kwargs) self._pending_work_items[self._queue_count] = w self._work_ids.put(self._queue_count) self._queue_count += 1 # Wake up queue management thread self._result_queue.put(None) self._start_queue_management_thread() return f
def_start_queue_management_thread(self): # When the executor gets lost, the weakref callback will wake up # the queue management thread. defweakref_cb(_, q=self._result_queue): q.put(None)
if self._queue_management_thread isNone: # Start the processes so that their sentinels are known. self._adjust_process_count() self._queue_management_thread = threading.Thread( target=_queue_management_worker, args=(weakref.ref(self, weakref_cb), self._processes, self._pending_work_items, self._work_ids, self._call_queue, self._result_queue)) self._queue_management_thread.daemon = True self._queue_management_thread.start() _threads_queues[self._queue_management_thread] = self._result_queue
def_adjust_process_count(self): for _ inrange(len(self._processes), self._max_workers): p = multiprocessing.Process( target=_process_worker, args=(self._call_queue, self._result_queue)) p.start() self._processes[p.pid] = p
def_process_worker(call_queue, result_queue): """Evaluates calls from call_queue and places the results in result_queue. This worker is run in a separate process. Args: call_queue: A multiprocessing.Queue of _CallItems that will be read and evaluated by the worker. result_queue: A multiprocessing.Queue of _ResultItems that will written to by the worker. shutdown: A multiprocessing.Event that will be set as a signal to the worker that it should exit when call_queue is empty. """ whileTrue: call_item = call_queue.get(block=True) if call_item isNone: # Wake up queue management thread result_queue.put(os.getpid()) return try: r = call_item.fn(*call_item.args, **call_item.kwargs) except BaseException as e: exc = _ExceptionWithTraceback(e, e.__traceback__) result_queue.put(_ResultItem(call_item.work_id, exception=exc)) else: result_queue.put(_ResultItem(call_item.work_id, result=r))
def_start_queue_management_thread(self): # When the executor gets lost, the weakref callback will wake up # the queue management thread. defweakref_cb(_, q=self._result_queue): q.put(None)
if self._queue_management_thread isNone: # Start the processes so that their sentinels are known. self._adjust_process_count() self._queue_management_thread = threading.Thread( target=_queue_management_worker, args=(weakref.ref(self, weakref_cb), self._processes, self._pending_work_items, self._work_ids, self._call_queue, self._result_queue)) self._queue_management_thread.daemon = True self._queue_management_thread.start() _threads_queues[self._queue_management_thread] = self._result_queue
def_queue_management_worker(executor_reference, processes, pending_work_items, work_ids_queue, call_queue, result_queue): """Manages the communication between this process and the worker processes. This function is run in a local thread. executor_reference: A weakref.ref to the ProcessPoolExecutor that owns Args: process: A list of the multiprocessing.Process instances used as this thread. Used to determine if the ProcessPoolExecutor has been garbage collected and that this function can exit. workers. pending_work_items: A dict mapping work ids to _WorkItems e.g. {5: <_WorkItem...>, 6: <_WorkItem...>, ...} work_ids_queue: A queue.Queue of work ids e.g. Queue([5, 6, ...]). call_queue: A multiprocessing.Queue that will be filled with _CallItems derived from _WorkItems for processing by the process workers. result_queue: A multiprocessing.Queue of _ResultItems generated by the process workers. """ executor = None
defshutting_down(): return _shutdown or executor isNoneor executor._shutdown_thread
defshutdown_worker(): # This is an upper bound nb_children_alive = sum(p.is_alive() for p in processes.values()) for i inrange(0, nb_children_alive): call_queue.put_nowait(None) # Release the queue's resources as soon as possible. call_queue.close() # If .join() is not called on the created processes then # some multiprocessing.Queue methods may deadlock on Mac OS X. for p in processes.values(): p.join()
sentinels = [p.sentinel for p in processes.values()] assert sentinels ready = wait([reader] + sentinels) if reader in ready: result_item = reader.recv() else: # Mark the process pool broken so that submits fail right now. executor = executor_reference() if executor isnotNone: executor._broken = True executor._shutdown_thread = True executor = None # All futures in flight must be marked failed for work_id, work_item in pending_work_items.items(): work_item.future.set_exception( BrokenProcessPool( "A process in the process pool was " "terminated abruptly while the future was " "running or pending." )) # Delete references to object. See issue16284 del work_item pending_work_items.clear() # Terminate remaining workers forcibly: the queues or their # locks may be in a dirty state and block forever. for p in processes.values(): p.terminate() shutdown_worker() return ifisinstance(result_item, int): # Clean shutdown of a worker using its PID # (avoids marking the executor broken) assert shutting_down() p = processes.pop(result_item) p.join() ifnot processes: shutdown_worker() return elif result_item isnotNone: work_item = pending_work_items.pop(result_item.work_id, None) # work_item can be None if another process terminated (see above) if work_item isnotNone: if result_item.exception: work_item.future.set_exception(result_item.exception) else: work_item.future.set_result(result_item.result) # Delete references to object. See issue16284 del work_item # Check whether we should start shutting down. executor = executor_reference() # No more work items can be added if: # - The interpreter is shutting down OR # - The executor that owns this worker has been collected OR # - The executor that owns this worker has been shutdown. if shutting_down(): try: # Since no new work items can be added, it is safe to shutdown # this thread if there are no pending work items. ifnot pending_work_items: shutdown_worker() return except Full: # This is not a problem: we will eventually be woken up (in # result_queue.get()) and be able to send a sentinel again. pass executor = None
def_add_call_item_to_queue(pending_work_items, work_ids, call_queue): """Fills call_queue with _WorkItems from pending_work_items. This function never blocks. Args: pending_work_items: A dict mapping work ids to _WorkItems e.g. {5: <_WorkItem...>, 6: <_WorkItem...>, ...} work_ids: A queue.Queue of work ids e.g. Queue([5, 6, ...]). Work ids are consumed and the corresponding _WorkItems from pending_work_items are transformed into _CallItems and put in call_queue. call_queue: A multiprocessing.Queue that will be filled with _CallItems derived from _WorkItems. """ whileTrue: if call_queue.full(): return try: work_id = work_ids.get(block=False) except queue.Empty: return else: work_item = pending_work_items[work_id]
if work_item.future.set_running_or_notify_cancel(): call_queue.put(_CallItem(work_id, work_item.fn, work_item.args, work_item.kwargs), block=True) else: del pending_work_items[work_id] continue
defset_running_or_notify_cancel(self): """Mark the future as running or process any cancel notifications. Should only be used by Executor implementations and unit tests. If the future has been cancelled (cancel() was called and returned True) then any threads waiting on the future completing (though calls to as_completed() or wait()) are notified and False is returned. If the future was not cancelled then it is put in the running state (future calls to running() will return True) and True is returned. This method should be called by Executor implementations before executing the work associated with this future. If this method returns False then the work should not be executed. Returns: False if the Future was cancelled, True otherwise. Raises: RuntimeError: if this method was already called or if set_result() or set_exception() was called. """ with self._condition: if self._state == CANCELLED: self._state = CANCELLED_AND_NOTIFIED for waiter in self._waiters: waiter.add_cancelled(self) # self._condition.notify_all() is not necessary because # self.cancel() triggers a notification. returnFalse elif self._state == PENDING: self._state = RUNNING returnTrue else: LOGGER.critical('Future %s in unexpected state: %s', id(self), self._state) raise RuntimeError('Future in unexpected state')
def_queue_management_worker(executor_reference, processes, pending_work_items, work_ids_queue, call_queue, result_queue): """Manages the communication between this process and the worker processes. This function is run in a local thread. executor_reference: A weakref.ref to the ProcessPoolExecutor that owns Args: process: A list of the multiprocessing.Process instances used as this thread. Used to determine if the ProcessPoolExecutor has been garbage collected and that this function can exit. workers. pending_work_items: A dict mapping work ids to _WorkItems e.g. {5: <_WorkItem...>, 6: <_WorkItem...>, ...} work_ids_queue: A queue.Queue of work ids e.g. Queue([5, 6, ...]). call_queue: A multiprocessing.Queue that will be filled with _CallItems derived from _WorkItems for processing by the process workers. result_queue: A multiprocessing.Queue of _ResultItems generated by the process workers. """ executor = None
defshutting_down(): return _shutdown or executor isNoneor executor._shutdown_thread
defshutdown_worker(): # This is an upper bound nb_children_alive = sum(p.is_alive() for p in processes.values()) for i inrange(0, nb_children_alive): call_queue.put_nowait(None) # Release the queue's resources as soon as possible. call_queue.close() # If .join() is not called on the created processes then # some multiprocessing.Queue methods may deadlock on Mac OS X. for p in processes.values(): p.join()
sentinels = [p.sentinel for p in processes.values()] assert sentinels ready = wait([reader] + sentinels) if reader in ready: result_item = reader.recv() else: # Mark the process pool broken so that submits fail right now. executor = executor_reference() if executor isnotNone: executor._broken = True executor._shutdown_thread = True executor = None # All futures in flight must be marked failed for work_id, work_item in pending_work_items.items(): work_item.future.set_exception( BrokenProcessPool( "A process in the process pool was " "terminated abruptly while the future was " "running or pending." )) # Delete references to object. See issue16284 del work_item pending_work_items.clear() # Terminate remaining workers forcibly: the queues or their # locks may be in a dirty state and block forever. for p in processes.values(): p.terminate() shutdown_worker() return ifisinstance(result_item, int): # Clean shutdown of a worker using its PID # (avoids marking the executor broken) assert shutting_down() p = processes.pop(result_item) p.join() ifnot processes: shutdown_worker() return elif result_item isnotNone: work_item = pending_work_items.pop(result_item.work_id, None) # work_item can be None if another process terminated (see above) if work_item isnotNone: if result_item.exception: work_item.future.set_exception(result_item.exception) else: work_item.future.set_result(result_item.result) # Delete references to object. See issue16284 del work_item # Check whether we should start shutting down. executor = executor_reference() # No more work items can be added if: # - The interpreter is shutting down OR # - The executor that owns this worker has been collected OR # - The executor that owns this worker has been shutdown. if shutting_down(): try: # Since no new work items can be added, it is safe to shutdown # this thread if there are no pending work items. ifnot pending_work_items: shutdown_worker() return except Full: # This is not a problem: we will eventually be woken up (in # result_queue.get()) and be able to send a sentinel again. pass executor = None
result_item 变量
我们看看
首先,大家可能在这里有点疑问了
1 2 3 4 5
sentinels = [p.sentinel for p in processes.values()] assert sentinels ready = wait([reader] + sentinels)
defwait(object_list, timeout=None): ''' Wait till an object in object_list is ready/readable. Returns list of those objects in object_list which are ready/readable. ''' with _WaitSelector() as selector: for obj in object_list: selector.register(obj, selectors.EVENT_READ)
if timeout isnotNone: deadline = time.time() + timeout
whileTrue: ready = selector.select(timeout) if ready: return [key.fileobj for (key, events) in ready] else: if timeout isnotNone: timeout = deadline - time.time() if timeout < 0: return ready
ready = wait([reader] + sentinels) if reader in ready: result_item = reader.recv() else: # Mark the process pool broken so that submits fail right now. executor = executor_reference() if executor isnotNone: executor._broken = True executor._shutdown_thread = True executor = None # All futures in flight must be marked failed for work_id, work_item in pending_work_items.items(): work_item.future.set_exception( BrokenProcessPool( "A process in the process pool was " "terminated abruptly while the future was " "running or pending." )) # Delete references to object. See issue16284 del work_item pending_work_items.clear() # Terminate remaining workers forcibly: the queues or their # locks may be in a dirty state and block forever. for p in processes.values(): p.terminate() shutdown_worker() return
ifisinstance(result_item, int): # Clean shutdown of a worker using its PID # (avoids marking the executor broken) assert shutting_down() p = processes.pop(result_item) p.join() ifnot processes: shutdown_worker() return elif result_item isnotNone: work_item = pending_work_items.pop(result_item.work_id, None) # work_item can be None if another process terminated (see above) if work_item isnotNone: if result_item.exception: work_item.future.set_exception(result_item.exception) else: work_item.future.set_result(result_item.result) # Delete references to object. See issue16284 del work_item
首先,如果 result_item 变量是 int 类型的话,不知道大家还记不记得在 _process_worker 函数中有这样一段逻辑
1 2 3 4 5 6
call_item = call_queue.get(block=True) if call_item isNone: # Wake up queue management thread result_queue.put(os.getpid()) return
当调用队列中没有新的任务时,将进程 pid 放入 result_queue 中。那么我们 result_item 如果值为 int 那么意味着,我们之前任务处理工作已经完毕,于是开始清理,关闭我们的进程池。