如何在cdef中等待?

2024-02-09

我有这个 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。真正的项目设置是这样的:

  1. 有规则的 Bison 解析器。规则给出了对特制结构的引用,我们称之为ParserState。该结构包含对回调的引用,当规则匹配时解析器将调用这些回调。

  2. 在 Cython 代码中,有一个类,我们称之为Parser,该包的用户应该扩展以创建他们的自定义解析器。这个类有一些方法,然后需要从回调中调用ParserState.

  3. 解析应该像这样发生:

    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'ed 或由事件循环运行。 Acdef功能不能await,但它可以构造并返回一个协程。

您的实际问题是将同步代码与异步代码混合在一起。例证:

async def example():
    loop.run_until_complete(py_call_foo())

这类似于将子例程放入线程中,但从不启动它。 即使启动了,这也是一个死锁:同步部分会阻止异步部分运行。


异步代码必须是awaited

An async def协程类似于def ...: yield生成器:调用它只是实例化它。您必须与其交互才能实际运行它:

def foo():
     print('running!')
     yield 1

bar = foo()  # no output!
print(next(bar))  # prints `running!` followed by `1`

同样,当你有一个async def协程,你必须await它或将其安排在事件循环中。自从asyncio.wait_for https://docs.python.org/3/library/asyncio-task.html#asyncio.wait_for产生一个协程,而你永远不会await或安排它,它不会运行。这就是造成这个问题的原因RuntimeWarning.

请注意,将协程放入的目的asyncio.wait_for纯粹是为了添加超时。它产生一个异步包装器,它必须是await'ed.

async def call_foo(callback):
    print('call_foo')
    await asyncio.wait_for(callback.foo(), timeout=2)

asyncio.get_event_loop().run_until_complete(call_foo(Callback()))

异步函数需要异步指令

异步编程的关键在于合作社:只有一个协程执行直到获得控制权。之后,另一个协程执行直到获得控制权。这意味着任何协程阻塞without让出控制也会阻止所有其他协程。

一般来说,如果某物在没有await上下文,它是阻塞的。尤其,loop.run_until_complete正在阻塞。您必须从同步函数调用它:

loop = asyncio.get_event_loop()

# async def function uses await
async def py_call_foo():
    await call_foo(Callback())

# non-await function is not async
def example():
    loop.run_until_complete(py_call_foo())

example()

从协程返回值

协程可以return结果就像常规函数一样。

async def make_result():
    await asyncio.sleep(0)
    return 1

If you await从另一个协程中,你可以直接得到返回值:

async def print_result():
    result = await make_result()
    print(result)  # prints 1

asyncio.get_event_loop().run_until_complete(print_result())

要从常规子例程内的协程获取值,请使用run_until_complete运行协程:

def print_result():
    result = asyncio.get_event_loop().run_until_complete(make_result())
    print(result)

print_result()

A cdef/cpdef函数不能是协程

Cython 通过以下方式支持协程yield from and await仅适用于 Python 函数。即使对于经典的协程来说,cdef不可能:

Error compiling Cython file:
------------------------------------------------------------
cdef call_foo(callback):
    print('call_foo')
    yield from asyncio.wait_for(callback.foo(), timeout=2)
   ^
------------------------------------------------------------

testbed.pyx:10:4: 'yield from' not supported here

你很好calling同步的cdef来自协程的函数。你很好调度来自 a 的协程cdef功能。 但你不能await从里面一个cdef函数,也不await a cdef功能。如果您需要这样做,如您的示例所示,请使用常规def功能。

但是,您可以构造并返回一个协程cdef功能。这可以让您await外部协程的结果:

# inner coroutine
async def pingpong(what):
    print('pingpong', what)
    await asyncio.sleep(0)
    return what

# cdef layer to instantiate and return coroutine
cdef make_pingpong():
    print('make_pingpong')
    return pingpong('nananana')

# outer coroutine
async def play():
    for i in range(3):
        result = await make_pingpong()
        print(i, '=>', result)

asyncio.get_event_loop().run_until_complete(play())

请注意,尽管await, make_pingpong不是协程。它只是一个协程工厂。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在cdef中等待? 的相关文章

随机推荐

  • 运行 Hadoop 作业时不是有效的 Jar

    我想运行 WordCount 示例 在eclipse中运行正确 在输出文件夹中存在输出文件 我制作了WordCount的jar文件并想通过命令运行它 hadoop jar WordCount jar Projects input Proje
  • 使用index.js在React中导入多个图像资源

    我一直在使用一种收集组件文件以供导出的模式index js文件放置在目录中 例如 index js file in components directory export Splash from Splash export Portfoli
  • 使用 Spring Boot 重命名 Liquibase 变更日志表

    我在用着Liquibase v 3 5 3 和 一起Spring 启动 v 1 5 3 我想使用 spring boot 属性文件更改 liquibase 变更日志表名称 我发现做到这一点的唯一方法是设置liquibase database
  • 找到具有相同权重的最大边数的生成树

    问题就在这里 给出一个带权无向连通图G 权重是恒定的 任务是提出一种算法 找到满足这两个条件的 G 的生成树的总权重 按优先级排序 生成树必须有相同权重的最大边数 与实际重复重量值无关 应最小化总生成树重量 这意味着 例如 权重为 120
  • 为什么.NET不验证BCL/CLR?

    BCL 和 CLR 中的所有 NET 程序集 以后仅使用 CLR 都是强命名和数字签名 https stackoverflow com questions 1334631 signing of net assemblies 提供数字证书是为
  • AngularJS,防止在茉莉花测试期间启动控制器上的 init 方法

    我有一个带有在实例化时启动的 init 方法的控制器 它做了很多对我的应用程序在实时环境中有用的事情 但这会扰乱我的单元测试间谍 在单元测试环境中实例化控制器时 有没有办法阻止它的调用 或者也许有一种方法可以在 web 应用程序上下文中自动
  • 这个R符号是什么意思?

    我将主题模型的文本上传到第四列 但显示的不是文本 而是此符号 当我将鼠标悬停在它上面时 我可以阅读文本 但是 我想确保这个符号不会误导我的结构主题模型分析 谢谢你 我刚刚运行主题模型没有问题 我认为这个符号表明单元格包含大文本 例如 几页单
  • R 将 RSelenium 驱动程序环境作为函数参数传递

    我可能没有看到明显的东西 无论如何我想创建函数来自动从远程驱动程序已处理的 URL 中提取文本 我想将 xpath 表达式和可以找到远程驱动程序的环境作为函数参数传递 library RSelenium url http stackover
  • ActiveMQ:如何使旧消息出队?

    我正在学习如何使用ActiveMQ 现在我们面临以下问题 假设我在 ActiveMQ 上有一个名为 topic test 的主题 它有两个订阅者 在特定时刻 我只有一个订阅者在等待消息 而生产者则为我上面提到的主题发送一条消息 好的 连接的
  • 没有模块前缀路由路径的 Rails 名称范围模型对象

    我对 Rails 路由器和表单生成器有一点问题 我的应用程序具有用于模型和控制器的命名空间模块 模块用于更轻松地抽象到另一个项目 我用在routes rb范围方法而不是命名空间 因为我不想有 丑陋 的路径助手 看起来像 scope modu
  • 如何使用 scrapy.Request 将另一个页面的元素加载到项目中

    我使用 Scrapy 创建了一个网络抓取工具 它能够从每张票证中抓取元素website http www vividseats com concerts awolnation tickets html但无法刮掉票价 因为页面上没有该票价 当
  • 如何将预先填充的 Default.realm 文件加载到设备上?

    我有一个领域文件 其中已填充了在设备上加载应用程序时需要的数据 我可以做什么来将领域文件获取到我的设备上进行测试 以及当有人从应用程序商店下载应用程序时 我需要做什么来确保它已经存在 我正在使用斯威夫特 Add your database
  • Intellij 14.1.4 CE spring boot 1.3.0.M1,spring-dev-tools 热重载不起作用

    刚刚使用 spring 初始化程序和 spring dev tools 启动了一个 spring boot 应用程序 然后导入到 Intellij 14 1 4 CE 中 我可以使用 gradle 很好地运行应用程序bootRun但无法使热
  • javascript图像覆盖在指定的div上

    我是 JavaScript 新手 实际上很新 这应该是我的第一个脚本 谁能向我解释如何在任何指定的固定宽度区域 例如 700x300px 上制作透明叠加层 您可以定义覆盖 例如 div class myoverlay contents di
  • 如何在高空照片中高效找到地平线?

    我试图检测从高空拍摄的图像中的地平线 以确定相机的方向 我也试图让它运行得更快 理想情况下 我希望能够在 Raspberry Pi 上实时处理帧 即每秒几帧 到目前为止我所采取的方法是基于这样一个事实 在高海拔地区 天空非常暗 如下所示 我
  • 通过 Comma IDE 使用时间轴可视化时出现“无法获取时间轴数据”

    执行答案后这个问题是关于如何设置时间可视化脚本 https stackoverflow com questions 58428899 do we need to modify a script to be able to use the t
  • 为所有下拉菜单调用值更改侦听器,而不仅仅是当前的

    我正在使用 MyFaces 1 1 我有两个
  • SET tx_isolation 与 SET TRANSACTION ISOLATION LEVEL

    在MySQL 尤其是5 5 中 我们似乎有两种不同的方法来设置事务隔离级别 我只是想知道我的想法是否正确 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ 完全一样 SET tx isolati
  • 无法将应用程序部署到 ios 8 设备

    刚刚将 iPad Mini 更新到 iOS 8 突然无法使用 XCode 6 0 1 调试我的应用程序 Xcode 中的错误是App installation failed with An unknown error has occurre
  • 如何在cdef中等待?

    我有这个 Cython 代码 简化 class Callback async def foo self print called cdef void call foo void callback print call foo asyncio