使用 Python asyncio 接口与 Cython 库
Using Python asyncio interfaces with Cython libraries
我有一个用 Cython 封装的 C++ 外部库。这个 C++ 库本身我无法更改。我想合并库以用作 Python 应用程序的一部分,该应用程序使用 asyncio 作为其主要过程控制。
Cython 库本质上是使用专有协议进行网络工作。然而,当库的事件处理程序在 python 中启动时,Cython 库会阻塞。我已经到了可以传递 Python 函数并接收从 C++ 库接收的事件的回调的阶段。如果我 运行 event_loop.run_in_executor.
中的事件处理程序,我可以解决将应用程序挂在库事件处理程序中的库
我的问题是,我怎样才能最好地对此进行建模以与与其接口相匹配的 asnycio 一起工作,而不是破解临时解决方案以使用 Cython 库方法?我研究过将其编写为 asyncio.Protocol 和 asyncio.Transport,然后使用 Cython 库作为其底层通信机制。然而,看起来要用一些猴子补丁让它看起来像一个套接字需要付出很多努力。有没有更好的方法或抽象来将包装器放在外部库上以使其与 asyncio 一起工作?
回答我自己的问题,据我所知,没有义务在 asyncio 中使用 Protocol 或 Transport 提供的抽象来构建应用程序。我发现对此的最佳建模是使用常规 class 并将其方法定义为异步。然后可以使 class 看起来像满足您要求的任何模式。如果您包装的代码没有与套接字相同的总体用例,这一点尤其重要。 asyncio 提供的抽象本身非常准系统。
对于像 Cython 包装的 C++ 阻塞代码这样复杂的事情,您将需要使用多处理来处理它。这是为了避免挂起解释器。 Asyncio 无法 运行 在不更改的情况下阻止代码。代码必须专门编写为与 asyncio 兼容。
我所做的是将整个阻塞代码(包括对象的构造)放入一个使用 event_loop.run_in_executor 执行的函数中。除此之外,我还使用了一个 unix 套接字来与进程通信以获取命令和回调数据。由于使用 unix 套接字,您可以在主应用程序中使用 asnycio 方法,管道也是如此。
这是我从多进程进程生产者向 asyncio 主进程发送 128 个字节得到的一些结果。数据以 10 毫秒的间隔生成。持续时间使用 time.perf_counter() 计时。以下结果以纳秒为单位。机器本身是 Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz 运行ning Linux kernel 4.10.17.
带 uvloop 的 Asyncio
count 10001.000000
mean 76435.956504
std 8887.459462
min 63608.000000
25% 71709.000000
50% 74104.000000
75% 79496.000000
max 287204.000000
标准 Asyncio 事件循环
count 10001.000000
mean 199741.937506
std 27900.377114
min 173321.000000
25% 185545.000000
50% 191839.000000
75% 205279.000000
max 529246.000000
我有一个用 Cython 封装的 C++ 外部库。这个 C++ 库本身我无法更改。我想合并库以用作 Python 应用程序的一部分,该应用程序使用 asyncio 作为其主要过程控制。
Cython 库本质上是使用专有协议进行网络工作。然而,当库的事件处理程序在 python 中启动时,Cython 库会阻塞。我已经到了可以传递 Python 函数并接收从 C++ 库接收的事件的回调的阶段。如果我 运行 event_loop.run_in_executor.
中的事件处理程序,我可以解决将应用程序挂在库事件处理程序中的库我的问题是,我怎样才能最好地对此进行建模以与与其接口相匹配的 asnycio 一起工作,而不是破解临时解决方案以使用 Cython 库方法?我研究过将其编写为 asyncio.Protocol 和 asyncio.Transport,然后使用 Cython 库作为其底层通信机制。然而,看起来要用一些猴子补丁让它看起来像一个套接字需要付出很多努力。有没有更好的方法或抽象来将包装器放在外部库上以使其与 asyncio 一起工作?
回答我自己的问题,据我所知,没有义务在 asyncio 中使用 Protocol 或 Transport 提供的抽象来构建应用程序。我发现对此的最佳建模是使用常规 class 并将其方法定义为异步。然后可以使 class 看起来像满足您要求的任何模式。如果您包装的代码没有与套接字相同的总体用例,这一点尤其重要。 asyncio 提供的抽象本身非常准系统。 对于像 Cython 包装的 C++ 阻塞代码这样复杂的事情,您将需要使用多处理来处理它。这是为了避免挂起解释器。 Asyncio 无法 运行 在不更改的情况下阻止代码。代码必须专门编写为与 asyncio 兼容。
我所做的是将整个阻塞代码(包括对象的构造)放入一个使用 event_loop.run_in_executor 执行的函数中。除此之外,我还使用了一个 unix 套接字来与进程通信以获取命令和回调数据。由于使用 unix 套接字,您可以在主应用程序中使用 asnycio 方法,管道也是如此。
这是我从多进程进程生产者向 asyncio 主进程发送 128 个字节得到的一些结果。数据以 10 毫秒的间隔生成。持续时间使用 time.perf_counter() 计时。以下结果以纳秒为单位。机器本身是 Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz 运行ning Linux kernel 4.10.17.
带 uvloop 的 Asyncio
count 10001.000000 mean 76435.956504 std 8887.459462 min 63608.000000 25% 71709.000000 50% 74104.000000 75% 79496.000000 max 287204.000000
标准 Asyncio 事件循环
count 10001.000000 mean 199741.937506 std 27900.377114 min 173321.000000 25% 185545.000000 50% 191839.000000 75% 205279.000000 max 529246.000000