问题描述
我知道如何:
- 用
threading
启动一个线程函数。问题:没有简单的方法可以返回结果 - get the return value 来自线程函数。问题:它是同步的
我想要实现的类似于 JavaScript 的
aFunctionThatReturnsAPromise()
.then(r => {// do something with the returned value when it is available})
// the code here runs synchronously right after aFunctionThatReturnsAPromise is started
在伪 Python 中,我会考虑类似的事情(将示例从 the answer 修改为链接线程)
import time
import concurrent.futures
def foo(bar):
print('hello {}'.format(bar))
time.sleep(10)
return 'foo'
def the_callback(something):
print(f"the thread returned {something}")
with concurrent.futures.ThreadPoolExecutor() as executor:
# submit the threaded call ...
future = executor.submit(foo,'world!')
# ... and set a callback
future.callback(the_callback,future.result()) # ← this is the made up part
# or,all in one: future = executor.submit(foo,'world!',callback=the_callback) # in which case the parameters probably would need to be passed the JS way
# the threaded call runs at its pace
# the following line is ran right after the call above
print("after submit")
# after some time (~10 seconds) the callback is finished (and has printed out what was passed to it)
# there should probably be some kind of join() so that the scripts waits until the thread is done
如果可能的话,我想留在线程中(它们按照自己的节奏做事,我不在乎它们何时完成),而不是 asyncio
(我必须在其中明确 await
单线程)
解决方法
您可以使用 concurrent.futures
库中的 add_done_callback,因此您可以像这样修改您的示例:
def the_callback(something):
print(f"the thread returned {something.result()}")
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(foo,'world!')
future.add_done_callback(the_callback)
,
您可以使用 concurrent.futures.dd_done_callback
,如下所示。回调必须是一个带有单个参数的可调用对象,即 Future
实例 - 并且它必须从中获取结果,如图所示。该示例还向其添加了一些附加信息,回调函数使用这些信息打印其消息。
注意回调函数将被并发调用,因此如果涉及共享资源,则应采取通常的互斥锁预防措施。这在示例中没有完成,因此有时打印的输出会混乱。
from concurrent import futures
import random
import time
def foo(bar,delay):
print(f'hello {bar} - {delay}')
time.sleep(delay)
return bar
def the_callback(fn):
if fn.cancelled():
print(f'args {fn.args}: canceled')
elif fn.done():
error = fn.exception()
if error:
print(f'args {fn.args}: caused error {erro}')
else:
print(f'args {fn.args}: returned: {fn.result()}')
with futures.ThreadPoolExecutor(max_workers=2) as executor:
for name in ('foo','bar','bas'):
delay = random.randint(1,5)
f = executor.submit(foo,name,delay)
f.args = name,delay
f.add_done_callback(the_callback)
print('fini')
示例输出:
hello foo - 5
hello bar - 3
args ('bar',3): returned: bar
hello bas - 4
args ('foo',5): returned: foo
args ('bas',4): returned: bas
fini