如何将asyncio与其他OS线程同步?

时间:2018-11-05 16:11:51

标签: python multithreading synchronization python-asyncio

我有一个具有一个主线程的程序,在该程序中产生了另一个使用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线程之间的同步?

1 个答案:

答案 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内部。