1、异步协程间通信方式类型
协程(Coroutine) 是异步编程中的基本代码单元。多个协程通常并发执行以提高运行效率。经常遇到的情形,协程之间需要通信,如多个协程需要访问修改同1个数据,或者1个协程需要等待另1个协程,等等。 对于协程之间通信需求,标准库asyncio模块除了提供了异步队列(asyncio.queue) 用于协程间数据传递, 还提供了几种同步通信对象。
同步原语 | 说明 |
---|---|
Lock | 同步锁,同时只允许 1个用户访问资源 |
Semaphore | 信号量,同时允许多个用户访问资源 |
Event | 信号事件机制,同时允许 1个用户访问资源 |
Condition | 类似于Lock与Event的组合 |
Barrier | 用于wait多个子线程并行运行 |
2、使用异步队列通信
主线程向协程,协程之间通信最好的方式是 queue, 与标准库的Queue模块不同,asyncio.Queue模块支持 async/await 从队列存取数据。
1)Asyncio.Queue 主要方法与属性如下;
- get()方法: 注意应该与await 一起使用, data = await queue.get(), 否则会报错。如果不在协程函数中读数据,使用 get_nowait() 方法。
- put()方法,协程函数中,await queue.put(data), 非协程函数,用 queue.put_nowait(data)
- join()方法: 阻塞queue,直到队列中的所有元素都被取出并处理。
- 当1个元素被添加到队列后,unfinished tasks 总数增加
- 当1个消费者协程调用 task_done()时,表示其取出了1个元素,与该元素相关的工作已经完成,unfinished task 减少1。
- 当unfinished task降为零, join()将解除阻塞。
- 其它方法:
qsize(), full(), empty(),maxsize() 与标准库queue使用相同。
2)示例
import asyncio
import random
async def worker(name, queue):
while not queue.empty():
n =await queue.get()
print(f"worker get {
n} from queue")
await asyncio.sleep(0.5)
queue.task_done() # 处理完1个元素,应发送task_done()
async def main():
# Create a queue that we will use to store our "workload".
qu = asyncio.Queue()
for _ in range(10):
qu.put_nowait(random.randrange(1,100,1))
task = asyncio.create_task(worker("worker",qu))
qu.join() # 阻塞队列,直到unfinished task 为0
# 生成异步任务集合
await asyncio.gather(task,return_exceptions=True)
print(qu.qsize())
if __name__ == "__main__":
asyncio.run(main())
3、同步通信
用于同步通信的对象包括: Lock, Event, Condition, Smartphore,Barrier, 与多线程使用方式基本相同。
1) Lock同步锁
用于控制多个协程访问同1个资源,避免同抢
通过示例了解其用法:
import asyncio
import random
counter = 0 # 全局变量
async def foo(num, lock):
await asyncio.sleep(1 + random.random())
await lock.acquire() # 获取锁
try:
global counter
print("coro foo is running, get ", num)
counter += 1
print("counter: "<