如何一次只运行一次协程,而不管它被调用了多少次?

时间:2020-07-17 10:50:44

标签: python python-asyncio

比方说,我有一个电子广告牌,应该显示最新的信息,该信息每秒可能更改一次以上。广告牌的更新例程大约需要10秒钟,因此每次调用和阻止它都是不理想的。

因此,让我们将其设为异步协程,并使用>>> df = pd.merge(df, prev_identifiers, how='left', on='date') >>> df['is_new'] = df.apply(lambda row: row['identifier'] not in row['identifier_prev'], axis=1) >>> print(df) date identifier value identifier_prev is_new 0 2019-12-31 a1 10 True 1 2019-12-31 a2 20 True 2 2019-12-31 a3 30 True 3 2020-01-31 a1 40 a1,a2,a3 False 4 2020-01-31 a2 50 a1,a2,a3 False 5 2020-01-31 a4 60 a1,a2,a3 True 6 2020-01-31 a5 60 a1,a2,a3 True 7 2020-02-28 a1 70 a1,a2,a4,a5 False 8 2020-02-28 a4 80 a1,a2,a4,a5 False 9 2020-02-28 a3 90 a1,a2,a4,a5 True 进行调用。这样一来,就有可能同时运行多个更新协程,甚至可能引入竞争条件,在这些条件下,最后的更新被延迟的先前更新所覆盖。

是否有一种整洁的Python方式来处理此问题?

我考虑过一个内部的未来,在调用update时,可以将其启动或等待完成,然后再调用它。但这不会再次阻止更新吗?

asyncio.ensure_future

1 个答案:

答案 0 :(得分:1)

您可以使用带有几个布尔标志的类来管理更新。像这样:

#! python3.8

import asyncio
import random

class Updater:
    def __init__(self):
        self.in_process = False
        self.pending = False
        
    async def request(self):
        if self.in_process:
            self.pending = True
            return
        self.pending = False
        self.in_process = True
        await asyncio.sleep(2.0)  # Ten seconds is too boring
        self.in_process = False
        print("...updated")
        if self.pending:
            asyncio.create_task(self.request())
        
async def main():
    up = Updater()
    while True:
        seconds = random.random() * 4.0
        await asyncio.sleep(seconds)
        print("request")
        asyncio.create_task(up.request())
        
asyncio.run(main())
        

第一个请求进入时,更新程序将运行。如果在第一个请求处理过程中出现另一个请求,它将设置“ pending”标志并返回。

在更新功能结束时,它检查是否有任何新请求挂起;如果是这样,它将重新安排自己的时间。如果没有,那一切都完成了。在这种情况下,直到main()函数发出新请求后,它才会再次运行。

实际上,“ pending”标志是一个请求队列,最大队列深度为1。多个请求被序列化,因此一次运行的请求不超过一个。它不在乎请求到达的速度是否超过其处理速度;它会处理所有可能的东西,然后丢弃其余的东西。