文档没有保证回调函数在哪个线程中运行。唯一的已记录保证是回调函数将在添加回调函数的进程所属的线程中运行,但这可能是任何线程,因为您正在使用ThreadPoolExecutor而不是ProcessPoolExecutor:
已添加的可调用项按添加顺序调用,并始终在添加它们的进程所属的线程中调用。
在当前的ThreadPoolExecutor实现中,回调函数执行的线程取决于添加回调时
Future
的状态以及
Future
是否被取消。这些是实现细节;您不应该依赖它们,因为它们可能在不同的Python实现或不同版本中有所不同,并且它们可能会在没有通知的情况下发生更改。
如果在
Future
完成后添加回调,则回调将在您调用
add_done_callback
的任何线程中执行。您可以通过查看
add_done_callback
源代码来了解这一点:
def add_done_callback(self, fn):
"""Attaches a callable that will be called when the future finishes.
Args:
fn: A callable that will be called with this future as its only
argument when the future completes or is cancelled. The callable
will always be called by a thread in the same process in which
it was added. If the future has already completed or been
cancelled then the callable will be called immediately. These
callables are called in the order that they were added.
"""
with self._condition:
if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]:
self._done_callbacks.append(fn)
return
fn(self)
如果
Future
的状态表明它已被取消或已完成,则
fn
会立即在当前执行线程中调用。否则,它将被添加到内部回调列表中,在
Future
完成时运行。
例如:
>>> def func(*args):
... time.sleep(5)
... print("func {}".format(threading.current_thread()))
>>> def cb(a): print("cb {}".format(threading.current_thread()))
...
>>> fut = ex.submit(func)
>>> func <Thread(Thread-1, started daemon 140084551563008)>
>>> fut = e.add_done_callback(cb)
cb <_MainThread(MainThread, started 140084622018368)>
如果通过成功的
cancel
调用取消了一个未来,那么执行取消操作的线程将立即调用所有回调函数:
def cancel(self):
"""Cancel the future if possible.
Returns True if the future was cancelled, False otherwise. A future
cannot be cancelled if it is running or has already completed.
"""
with self._condition:
if self._state in [RUNNING, FINISHED]:
return False
if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
return True
self._state = CANCELLED
self._condition.notify_all()
self._invoke_callbacks()
return True
否则,回调函数将由执行 future 任务的线程调用。
ProcessPoolExecutor
呢?回调函数的get_ident
也是不同的。 - Winand