我有一个Cython代码(简体):
class Callback:
async def foo(self):
print('called')
cdef void call_foo(void* callback):
print('call_foo')
asyncio.wait_for(<object>callback.foo())
async def py_call_foo():
call_foo(Callback())
async def example():
loop.run_until_complete(py_call_foo())
但是发生了什么:我得到RuntimeWarning: coroutine Callback.foo was never awaited
。而且,事实上,它从未被调用过。但是,call_foo
被调用。在
你知道发生了什么吗/如何让它真正等待Callback.foo
完成吗?在
在上面的例子中缺少一些重要的细节:特别是,从call_foo
获取返回值确实很困难。真正的项目设置是这样的:
有规则的Bison解析器。规则被赋予对巧尽心思构建的结构的引用,我们称之为ParserState
。此结构包含对回调的引用,当规则匹配时,解析器将调用这些回调。
在Cython代码中,有一个类,我们称之为Parser
,包的用户应该扩展它来生成他们的自定义解析器。这个类的方法需要从ParserState
的回调调用。
解析应该是这样进行的:
async def parse_file(file, parser):
cdef ParserState state = allocate_parser_state(
rule_callbacks,
parser,
file,
)
parse_with_bison(state)
回调的形状一般:
ctypedef void(callback*)(char* text, void* parser)
我不得不承认,我不知道asyncio
是如何实现await
,因此我不知道一般情况下是否可以用我现有的设置来实现这一点。不过,我的最终目标是,多个Python函数能够迭代地解析不同的文件,或多或少都是在同一时间。在
TLDR:
协程必须
await
'或由事件循环运行。cdef
函数不能await
,但它可以构造并返回一个协程。在您的实际问题是将同步代码与异步代码混合在一起。典型案例:
这类似于将子例程放入线程中,但从不启动它。 即使启动时,这也是一个死锁:同步部分会阻止异步部分的运行。在
异步代码必须是
await
ed
^{pr2}$async def
协程与def ...: yield
生成器类似:调用它只会实例化它。您必须与它交互才能实际运行它:类似地,当您有一个} 生成一个协程,而您从未
async def
协程时,您必须await
或者将其安排在事件循环中。由于^{await
或调度它,所以它不运行。这就是RuntimeWarning
的原因。在注意,将一个协程放入
asyncio.wait_for
的目的纯粹是为了添加一个超时。它产生一个异步包装器,必须await
'异步函数需要异步指令
异步编程的关键在于它是一个协同程序,直到产生控制为止。然后,另一个协同程序执行,直到产生控制。这意味着,任何一个阻止而不产生的协程也会阻塞所有其他协程。在
一般来说,如果某个东西在没有
await
上下文的情况下执行工作,则它是阻塞的。值得注意的是,loop.run_until_complete
正在阻塞。必须从同步函数调用它:从协同程序返回值
协同程序可以
return
产生类似于正则函数的结果。在如果您
await
它来自另一个协程,则直接获得返回值:要从常规子例程内的协同例程中获取值,请使用
run_until_complete
来运行协同例程:cdef/cpdef
函数不能是协程Cython只对Python函数通过
yield from
和await
支持协同路由。即使对于经典的协同程序,也不可能有cdef
:从协同程序调用同步函数是非常好的。从
cdef
函数中调度一个协程是非常好的。 但不能从cdef
函数内部await
,也不能await
一个cdef
函数。如果您需要这样做,如您的示例中所示,请使用常规的def
函数。在但是,您可以在
cdef
函数中构造并返回一个协程。这允许您await
生成一个外部协同程序:注意,尽管
await
,make_pingpong
不是一个协程。它只是一个合作的工厂。在相关问题 更多 >
编程相关推荐