使用Python asyncio接口与Cython库

Using Python asyncio interfaces with Cython libraries

我有一个C++的外部库,它已经被Cython包住了。这个C++库本身无法更改。我想结合这个库,将其作为使用Asyncio作为其主要进程控制的Python应用程序的一部分使用。

Cython库基本上用一个专有的协议来进行网络工作。但是,当库的事件处理程序在python中启动时,cython库正在阻塞。我已经把它带到了一个阶段,我可以通过Python函数接收从C++库接收到的事件的回调。如果我在"执行器"中的event_loop.run_内运行事件处理程序,我可以解决在库事件处理程序处挂起应用程序的库。

我的问题是,我如何才能最好地对它进行建模,使其能够与ASNYCIO配合使用,而不是对使用Cython库方法的临时解决方案进行黑客攻击?我曾经研究过将其作为asyncio.protocol和asyncio.transport编写,然后使用cython库作为底层通信机制。不过,看起来要想让它看起来像一个插座,需要花费很多精力来修补猴子。是否有更好的方法或抽象方法将包装器放在外部库上,使其与Asyncio一起工作?


为了回答我自己的问题,就我所见,没有义务使用协议或异步传输提供的抽象来构造应用程序。对于这个问题,我发现最好的建模方法是使用一个定义为Async的常规类。然后可以使类看起来像符合您要求的任何模式。如果要包装的代码与套接字的总体使用情形不同,那么这一点尤其重要。异步提供的抽象本身是非常简单的。对于复杂的东西,比如Cython包的C++阻塞代码,你需要用多重处理来处理它。这是为了避免挂译员。Asyncio不能在不进行更改的情况下运行阻塞代码。必须专门编写与异步兼容的代码。

我所做的是将整个阻塞代码(包括对象的构造)放入一个函数中,该函数是在执行器中使用event_loop.run_执行的。除此之外,我还使用了一个Unix套接字来与进程通信,以获取命令和回调数据。由于使用了Unix套接字,您可以在主应用程序中使用asnycio方法,管道也是如此。

下面是我从多进程进程生成器向Asyncio主进程发送128个字节得到的一些结果。数据以10毫秒的间隔生成。使用time.perf_counter()对持续时间进行了计时。下面的结果以纳秒为单位。这台机器本身就是运行Linux内核4.10.17的Intel(R)Core(tm)i7-2600 [email protected]

带uvloop的异步

1
2
3
4
5
6
7
8
count   10001.000000
mean    76435.956504
std      8887.459462
min     63608.000000
25%     71709.000000
50%     74104.000000
75%     79496.000000
max    287204.000000

标准异步事件循环

1
2
3
4
5
6
7
8
count   10001.000000
mean   199741.937506
std     27900.377114
min    173321.000000
25%    185545.000000
50%    191839.000000
75%    205279.000000
max    529246.000000