我是 asyncio 的新手,需要一些有关如何构建以下方案的建议。我有一个 Cython 扩展,它接受一个回调。每次新事件到来时都会执行前者。然而,启动收集那些事件的机制是阻塞操作,即它阻塞主线程。
Cython扩展还接受asyncio.Queue
,并从回调中调用put_nowait
方法。现在我想设置队列的使用者来处理事件。这可能是场景背后可能的伪代码:
aioq = asyncio.Queue(1000)
cext = CythonExtension(aioq)
def c(aioq):
while not aioq.empty():
e = yield from aioq.get()
loop.create_task(c(aioq))
# i'm not sure how to run the event loop
# and keep on initializing the cython extension
# because this call also blocks...
#loop.run_forever()
# so i tried this.
loop.run_in_executor(None, cext.start) <- this is a blocking operation
# start the event loop
loop.run_forever()
当我运行示例时,asyncio
队列充满了事件,但c
任务从未执行 - 我无法从队列中获取任何事件。非常感谢有关如何解决此问题的任何反馈或指示。
答案 0 :(得分:1)
常规Python代码将在执行一定数量的字节码操作后允许另一个线程转动。例如,请参阅this set of slides,其中讨论了该机制的改进,但也解释了该机制。
Cython不会执行字节码,因此永远不会触发此交换机制,因此Cython线程将无限期地阻塞(正如您所发现的那样)。绕过它的一个简单方法是将以下行添加到 The string is ��_o�Remove2
The string is ��_o�Remove23
The string is ��_o�Remove232
The string is ��_o�Remove232
中的主Cython循环中(因此它会定期执行):
cext.start
这个版本,然后立即尝试回收全局解释器锁(GIL),它允许其他线程执行(如果另一个线程执行,那么Cython将不得不等待)。
更好的选择是识别不需要GIL的Cython代码位(主要是使用C数据类型的位,而不是Python数据类型)并将它们包装在with nogil:
pass
块中。这将允许你的Cython代码继续做一些有用的东西,而另一个线程也运行。