python-如何将C函数实现为可等待的(协程)

python-如何将C函数实现为可等待的(协程),第1张

python-如何将C函数实现为可等待的(协程)

注意:此答案涵盖CPython和asyncio框架。但是,这些概念应适用于其他Python实现以及其他异步框架。

如何编写C函数,以便可以

await
使用它?

编写可以等待结果的C函数的最简单方法是使其返回已创建的等待对象,例如

asyncio.Future
。在返回之前
Future
,代码必须安排通过某种异步机制设置将来的结果。所有这些基于协程的方法都假定您的程序在某个事件循环下运行,该事件循环知道如何调度协程。

但是返回未来并不总是足够的-
也许我们想定义一个具有任意数量的悬挂点的对象。返回未来仅暂停一次(如果返回的未来未完成),一旦完成未来就恢复,仅此而已。等效于一个

asyncdef
包含多个对象的可等待对象不能通过返回Future
await
来实现,它必须实现协程通常实现的协议。这有点像实现自定义项的迭代器
__next__
,而不是生成器。

定义等待的自定义

要定义我们自己的等待类型,我们可以转到PEP
492,它确切指定了可以传递给的对象

await
。除了使用定义的Python函数之外
asyncdef
,用户定义类型可以通过定义
__await__
特殊方法使对象等待,该方法将Python /
C映射到结构的
tp_as_async.am_await
一部分
PyTypeObject

这意味着在Python / C中,您必须执行以下 *** 作:

  • tp_as_async
    扩展类型的字段指定一个非NULL值。
  • 使它的
    am_await
    成员指向C函数,该函数接受您的类型的实例并返回实现迭代器协议的另一个扩展类型的实例,即,定义
    tp_iter
    (通常定义为
    PyIter_Self
    )和
    tp_iternext
  • 迭代器
    tp_iternext
    必须推进协程的状态机。每个非例外的回报都
    tp_iternext
    对应一个暂停,最终
    StopIteration
    例外表示协程的最终回报。返回值存储在的
    value
    属性中
    StopIteration

为了使协程有用,它还必须能够与驱动它的事件循环进行通信,以便它可以指定挂起后何时恢复。由asyncio定义的大多数协程都期望在asyncio事件循环下运行,并在内部使用

asyncio.get_event_loop()
(和/或接受显式
loop
参数)来获取其服务。

协程示例

为了说明Python / C代码需要实现什么,让我们考虑用Python表示的简单协程

async def
,例如
asyncio.sleep()

async def my_sleep(n):    loop = asyncio.get_event_loop()    future = loop.create_future()    loop.call_later(n, future.set_result, None)    await future    # we get back here after the timeout has elapsed, and    # immediately return

my_sleep
创建一个
Future
,安排它在 n
秒内完成(其结果将被设置),并暂停自身直到将来完成。最后一部分使用
await
,其中的
awaitx
意思是“允许
x
决定我们现在将暂停还是继续执行”。不完整的将来总是决定暂停,而异步
Task
协程驱动程序特殊情况产生了将来无限期地暂停它们并将将来的完成与恢复任务联系起来的将来。其他事件循环(curio等)的挂起机制在细节上可以有所不同,但是基本思想是相同的:
await
是可选的执行挂起。

__await__()
返回一个发电机

要将其转换为C,我们必须摆脱魔术

async def
函数定义以及
await
悬挂点。删除
asyncdef
相当简单:等效的普通函数只需要返回一个实现的对象
__await__

def my_sleep(n):    return _MySleep(n)class _MySleep:    def __init__(self, n):        self.n = n    def __await__(self):        return _MySleepIter(self.n)

返回

__await__
_MySleep
对象的方法
my_sleep()
将由
await
*** 作员自动调用,以将可 等待的
对象(传递给的任何对象
await
)转换为迭代器。该迭代器将用于询问等待的对象是选择暂停还是提供值。这非常类似于该
for o inx
语句调用
x.__iter__()
可迭代对象 转换
x
为具体 迭代器的方式

当返回的迭代器选择暂停时,只需要产生一个值即可。值的含义(如果有的话)将由协程驱动程序解释,通常是事件循环的一部分。当迭代器选择停止执行并从返回时

await
,它需要停止迭代。将生成器用作便利迭代器实现,
_MySleepIter
如下所示:

def _MySleepIter(n):    loop = asyncio.get_event_loop()    future = loop.create_future()    loop.call_later(n, future.set_result, None)    # yield from future.__await__()    for x in future.__await__():        yield x

作为

await x
映射到
yield fromx.__await__()
,我们的发电机必须用尽返回的迭代器
future.__await__()
Future.__await__
如果将来未完成,则返回的迭代器将产生收益,否则返回将来的结果(在此我们忽略,但
yieldfrom
实际上提供了结果)。

__await__()
返回自定义迭代器

用C语言实现C语言的最终障碍

my_sleep
是使用生成器
_MySleepIter
。幸运的是,任何生成器都可以转换为有状态迭代器,
__next__
该迭代器将执行这段代码直到下一次等待或返回。
__next__
实现生成器代码的状态机版本,在该状态机中
yield
通过返回值并
return
通过raise来表示
StopIteration
。例如:

class _MySleepIter:    def __init__(self, n):        self.n = n        self.state = 0    def __iter__(self):  # an iterator has to define __iter__        return self    def __next__(self):        if self.state == 0: loop = asyncio.get_event_loop() self.future = loop.create_future() loop.call_later(self.n, self.future.set_result, None) self.state = 1        if self.state == 1: if not self.future.done():     return next(iter(self.future)) self.state = 2        if self.state == 2: raise StopIteration        raise AssertionError("invalid state")
翻译成C

上面有很多类型,但是可以使用,并且仅使用可以通过本机Python / C函数定义的构造。

实际上,将这两个类转换为C非常简单,但超出了此答案的范围。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5655533.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存