原文出自:Asyncio Synchronization Primitives Tutorial - Queues and Locks
在本教程中,我们将查看Asyncio编程冒险中可用的各种同步原语。我们将简要介绍为什么这些同步原语很重要,以及在简单的基于Asyncio的程序中使用它们的各种方法。
为什么这些很重要?
在编写并发系统时,您必须尝试确保您的程序没有称为 Race Condition 的小东西。当多个并发工作者同时尝试修改共享变量,数组等时,会发生竞争条件,并且由于时序问题,它们产生错误的结果。
由于存在这些竞争条件,我们必须利用称为同步原语的东西。当谈到Asyncio中的同步原语时,我们有一个数字可供选择。这些都基于线程模块等价物,并且往往具有与我们一起使用的相同API。
Locks
描述锁如何工作的最好类比是想象有一群人试图进入浴室。一个人进去并锁上门,这样做可以防止他人在你正在洗澡时进来。
在计算术语中我们锁定某些东西的时候,我们基本上阻止了其他人操作修改我们锁定的资源。
一个简单的locks示例
在这个例子中,我们将创建一个 asyncio.Lock() 实例,我们将尝试使用 with await lock 获取此锁。一旦我们的 Worker 获得了这个锁,我们就会执行我们的关键代码部分,然后继续释放我们刚刚获得的锁。
import asyncio
import time
async def my_worker(lock):
print("试图获得lock")
# 获得lock
with await lock:
# 运行代码的关键部分
print("目前已锁定")
time.sleep(2)
print("解锁的关键部分")
async def main():
# 实例化一个锁
lock = asyncio.Lock()
# 等待执行2个Worker协程,每个协程都传入相同的锁实例
await asyncio.wait([my_worker(lock), my_worker(lock)])
# 启动一个简单的循环并运行我们的main函数直到它完成
lock = asyncio.Lock()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
print("所有任务都完成了")
loop.close()
输出:
当我们运行这个时,你应该看到我们只有一个同时运行的Works能够一次运行.
Queues
在以同步方式进行通信时,asyncio提供了自己的基于队列的实现. 我们可以让生产者以同步的方式将事物推送到我们的队列中,并让消费者同时轮询这个队列以获取推送到它上面的任何东西。
简单的示例:
在这个例子中,我们将创建一个 newsProducer() 协程和一个 newsConsumer() 协程。
newsProducer() 协程 将会推送新的新闻项目到我们的同步队列,newsConsumer() 协程将尝试检索已被推入到所述队列的任何项目,然后在获得消息是打印出来。
import asyncio
import random
async def news_producer(my_queue):
while True:
await asyncio.sleep(1)
print("将新闻项目放入队列")
await my_queue.put(random.randint(1,5))
async def news_consumer(_id, my_queue):
print(my_queue)
while True:
print("消费者:{} 试图从队列中获取".format(_id))
item = await my_queue.get()
if item is None:
# 没有生产者发送的数据是,表面她已经完成了
break
print("消费者:{} 使用ID为 {} 的文章".format(_id, item))
loop = asyncio.get_event_loop()
my_queue = asyncio.Queue(loop=loop, maxsize=10)
try:
loop.run_until_complete(asyncio.gather(news_producer(my_queue),
news_consumer(1, my_queue), news_consumer(2, my_queue)))
except KeyboardInterrupt:
pass
finally:
loop.close()
输出
当我们尝试运行它时,您应该看到我们的生产者将项目推送到我们的队列中,然后我们的消费者彼此竞争以便将任何内容取出队列。
结论
如果您发现本教程有用或需要进一步的帮助,请在下面的评论部分告诉我们!