我有一个具有一个主线程的程序,在该程序中产生了另一个使用asyncio的线程。是否提供任何工具来同步这两个线程?如果一切都是异步的,我可以使用其同步原语来实现,例如:
///trait AuthenticatesUsers in Auth/LoginController
protected function sendLoginResponse(Request $request)
{
$request->session()->regenerate();
$this->clearLoginAttempts($request);
$products = Product::all();
$foo = Foo::all();
$bar = Bar::all();
return $this->authenticated($request, $this->guard()->user())
? : redirect()->intended($this->redirectPath())
->with('pr', $products)
->with('foo', $foo)
->with('bar', $bar);
}
但是,这不适用于多个线程。如果我仅使用import asyncio
async def taskA(lst, evt):
print(f'Appending 1')
lst.append(1)
evt.set()
async def taskB(lst, evt):
await evt.wait()
print('Retrieved:', lst.pop())
lst = []
evt = asyncio.Event()
asyncio.get_event_loop().run_until_complete(asyncio.gather(
taskA(lst, evt),
taskB(lst, evt),
))
,它将阻塞asyncio线程。我发现我可以将等待时间推迟到执行者身上:
threading.Event
但是,仅让执行程序线程等待互斥锁似乎是不自然的。这是应该完成的方式吗?还是有其他方法可以异步等待OS线程之间的同步?
答案 0 :(得分:1)
将异步协程与来自另一个线程的事件同步的简单方法是等待taskB中的asyncio.Event
,然后使用loop.call_soon_threadsafe
从taskA进行设置。
要能够在两者之间传递值和异常,可以使用期货。但是随后您发明了run_in_executor
的大部分内容。如果taskA的唯一工作是将任务从队列中取出,则最好将单个工作人员“池”用作其工作线程。然后,您可以按预期使用run_in_executor
:
worker = concurrent.futures.ThreadPoolExecutor(max_workers=1)
async def taskB(lst):
loop = asyncio.get_event_loop()
# or result = await ..., if taskA has a useful return value
# This will also propagate exceptions raised by taskA
await loop.run_in_executor(worker, taskA, lst)
print('Retrieved:', lst.pop())
语义与您使用显式队列的版本中的语义相同-队列仍然存在,只是在ThreadPoolExecutor
内部。