# Copyright 2009 Brian Quinlan. All Rights Reserved. # Licensed to PSF under a Contributor Agreement. """Implements ThreadPoolExecutor.""" __author__ = 'Brian Quinlan (brian@sweetapp.com)' import atexit from concurrent.futures import _base import itertools import queue import threading import weakref import os # Workers are created as daemon threads. This is done to allow the interpreter # to exit when there are still idle threads in a ThreadPoolExecutor's thread # pool (i.e. shutdown() was not called). However, allowing workers to die with # the interpreter has two undesirable properties: # - The workers would still be running during interpreter shutdown, # meaning that they would fail in unpredictable ways. # - The workers could be killed while evaluating a work item, which could # be bad if the callable being evaluated has external side-effects e.g. # writing to a file. # # To work around this problem, an exit handler is installed which tells the # workers to exit when their work queues are empty and then waits until the # threads finish. _threads_queues = weakref.WeakKeyDictionary() _shutdown = False def _python_exit(): global _shutdown _shutdown = True items = list(_threads_queues.items()) for t, q in items: q.put(None) for t, q in items: t.join() atexit.register(_python_exit) class _WorkItem(object): def __init__(self, future, fn, args, kwargs): self.future = future self.fn = fn self.args = args self.kwargs = kwargs def run(self): if not self.future.set_running_or_notify_cancel(): return try: result = self.fn(*self.args, **self.kwargs) except BaseException as exc: self.future.set_exception(exc) # Break a reference cycle with the exception 'exc' self = None else: self.future.set_result(result) def _worker(executor_reference, work_queue): try: while True: #线程的目标函数, 它会不断地从当前的队列里面取出任务并且执行, 注意: ##1. 我们提交了多少次, 只要不超过 max_threads 就会发起多少线程(_adjust_thread_count) ##2. 每个线程都会不断的从当前的 work queue提取任务, 如果队列已经为空就会一直阻塞, 不会终止 ##3. 因此每个线程执行哪些任务不是被安排好的, 有可能发起了线程但是他一直是阻塞的 work_item = work_queue.get(block=True) if work_item is not None: work_item.run() # Delete references to object. See issue16284 del work_item continue #下面代码的逻辑是: 如果get到None, 就调用弱引用,如果弱引用是None或者实例已经终止, 就终止线程(if条件下的return语句)并且在工作队列中加入None好让其他的线程也能get到None从而终止 #如果没有问题就要删除弱引用对象 #executor_reference是一个绑定了当前线程池实例的弱引用对象 #以下来自 weakref 的文档: ##-如果原始对象仍然存活,则可以通过调用引用对象来检索原始对象;如果引用的原始对象不再存在,则调用引用对象将得到 None. ##-如果提供了 回调 而且值不是 None ,并且返回的弱引用对象仍然存活,则在对象即将终结时将调用回调;弱引用对象将作为回调的唯一参数传递;指示物将不再可用. executor = executor_reference() # Exit if: # - The interpreter is shutting down OR # - The executor that owns the worker has been collected OR # - The executor that owns the worker has been shutdown. if _shutdown or executor is None or executor._shutdown: #注意即使线程池的实例被del了, 实例的work_queue依然会存在 # Notice other workers work_queue.put(None) return #注意删除 这个executor不会删除原始对象 #弱引用的对象被删除, 回调函数不会调用 del executor except BaseException: _base.LOGGER.critical('Exception in worker', exc_info=True) class ThreadPoolExecutor(_base.Executor): # Used to assign unique thread names when thread_name_prefix is not supplied. _counter = itertools.count().__next__ def __init__(self, max_workers=None, thread_name_prefix=''): """Initializes a new ThreadPoolExecutor instance. Args: max_workers: The maximum number of threads that can be used to execute the given calls. thread_name_prefix: An optional name prefix to give our threads. """ if max_workers is None: # Use this number because ThreadPoolExecutor is often # used to overlap I/O instead of CPU work. max_workers = (os.cpu_count() or 1) * 5 if max_workers <= 0: raise ValueError("max_workers must be greater than 0") self._max_workers = max_workers self._work_queue = queue.Queue() self._threads = set() self._shutdown = False self._shutdown_lock = threading.Lock() self._thread_name_prefix = (thread_name_prefix or ("ThreadPoolExecutor-%d" % self._counter())) def submit(self, fn, *args, **kwargs): #submit函数会立刻返回 with self._shutdown_lock: if self._shutdown: raise RuntimeError('cannot schedule new futures after shutdown') f = _base.Future() w = _WorkItem(f, fn, args, kwargs) #work queue被所有线程共享, 里面放入 _WorkItem对象 #_WotkItem对象有一个 属性是Future的实例, 用它来储存返回结果, 并且该实例会被submmit返回 self._work_queue.put(w) self._adjust_thread_count() return f submit.__doc__ = _base.Executor.submit.__doc__ def _adjust_thread_count(self): # When the executor gets lost, the weakref callback will wake up # the worker threads. #弱引用的回调函数 def weakref_cb(_, q=self._work_queue): q.put(None) # TODO(bquinlan): Should avoid creating new threads if there are more # idle threads than items in the work queue. num_threads = len(self._threads) if num_threads < self._max_workers: thread_name = '%s_%d' % (self._thread_name_prefix or self, num_threads) #线程的目标函数是 _worker, 这一函数有两个参数, 线程池本身的弱引用和当前的工作队列 t = threading.Thread(name=thread_name, target=_worker, args=(weakref.ref(self, weakref_cb), self._work_queue)) t.daemon = True t.start() self._threads.add(t) #创建映射 线程t : 当前队列 _threads_queues[t] = self._work_queue def shutdown(self, wait=True): with self._shutdown_lock: self._shutdown = True self._work_queue.put(None) if wait: for t in self._threads: t.join() shutdown.__doc__ = _base.Executor.shutdown.__doc__