异步Python中的精确一次初始化

2020-08-01 11:06:43

异步Python程序中的一种常见情况是异步初始化。某些资源必须只初始化一次才能使用,但初始化本身是异步的-例如异步数据库。让我们来谈谈几个解决方案。

初始化=FALSE异步定义ONE_TIME_SETUP():";请勿多次调用!";...。异步定义可能_Initialize():如果未初始化,则全局初始化:等待one_time_setup()Initialized=True。

初始化的理由是期望多次调用函数。但是,如果它可能是从并发任务中调用的,则会出现争用情况。如果第二个调用方在第一个调用方等待one_time_setup()时到达,则函数将被第二次调用。

因为Asyncio是协作的,所以第一个调用者在等待之前不会放弃对其他任务的控制,这意味着One_Time_Setup()永远不会被调用两次。但是,第二个调用者可能会在one_time_setup()完成之前返回。我们想要的是只调用one_time_setup()一次,但是在它返回之前调用者不会返回。

我的第一个想法是使用互斥锁。这将保护变量并防止后续调用者进行得太快。ONE_TIME_SETUP()仍在运行时到达的任务将在锁上阻塞。

INITIALIZED=FALSE INITIALIZED_LOCK=异步。Lock()异步def可能_initialize():使用initialized_lock全局初始化异步:如果未初始化:等待one_time_setup()initialized=True。

不幸的是,这有一个严重的缺点:异步锁与创建它们的循环相关联。因为Lock变量是全局变量,所以只能从加载模块的同一个循环中调用PROSECT_INITIALIZE()。Run()创建了一个新循环,因此它是不兼容的。

#创建循环:异步总是出错。Run(May_initialize())#重用循环:可能是一个错误循环=asyncio。Get_event_loop()循环。RUN_TRANSE_COMPLETE((可能_Initialize()

(IMHO,asyncio API包含显式循环对象是一个错误。这是一个低级概念,不可避免地会在高层抽象中泄漏。)。

解决方法是延迟创建锁。谢天谢地,创建alock本身不是异步的!

Initialized=false initialized_lock=None异步定义可能_Initialize():GLOBAL INITIALIZED,INITIALIZED_LOCK,如果不是INITIALIZED_LOCK:INITIALIZED_LOCK=asyncio。LOCK()与INITIALIZED_LOCK异步:如果未初始化:等待ONE_TIME_SETUP()INITIALIZED=True。

PthreadAPI提供了pthreadonce来解决这个问题,C++11也有类似的std::call_once。我们可以用一个类似未来的物体建造一些类似的东西。

多次等待协程是错误的,但是任务是类似于未来的对象,可以等待不止一次。至少在CPython上,它们也可以在其他循环中等待!因此,这不仅更简单,还解决了循环问题!

Def once(Func):Future=None异步def once_wrapper(*args,**kwargs):如果不是将来,则为非本地将来:Future=asyncio。Create_task(func(*args,**kwargs))返回等待将来返回ONCE_Wrapper。

对这篇文章有什么评论吗?通过发送电子邮件至~Skeeto/[email protected][邮件列表礼仪]开始我的公共收件箱中的讨论,或查看现有讨论。