快捷搜索:

知道了多线程和进程,你可能不知道协程的存在


知道了多线程和进程,你可能不知道协程的存在


@Author: runsen

协程是实现并发编程的一种方式。

https://docs.python.org/zh-cn/3/library/asyncio.html

一说并发,你肯定想到了多线程 , 多进程模型,没错,多线程 和 多进程,正是解决并发问题的经典模型之一

但是你了解过协程Coroutine吗?

协程:是单线程下的并发,又称微线程。

就是只有一个线程,如何提高速度,解决并发编程

英文名Coroutine。

协程比线程的单位更小——协程

注意协程这个概念完全是程序员自己想出来的东西,它对于操作系统来说根本不存在。操作系统只有进程和线程。

从一个demo学起

importtime

defprint_num(num):
print("Maoliisprinting"+str(num)+"nows")
time.sleep(1)
print("Maoliprints"+str(num)+"OK")

defmain(nums):
fornuminnums:
print_num(num)
%timemain([iforiinrange(1,6)])


Maoliisprinting1nows
Maoliprints1OK
Maoliisprinting2nows
Maoliprints2OK
Maoliisprinting3nows
Maoliprints3OK
Maoliisprinting4nows
Maoliprints4OK
Maoliisprinting5nows
Maoliprints5OK
Walltime:5s

%time 需要在jupyter notebook中运行。

上面代码是从上到下执行的。

下面将上面代码改为协程版

注意py版本3.7以上,主要使用的是asyncio

importasyncio

asyncdefprint_num(num):
print("Maoliisprinting"+str(num)+"nows")
awaitasyncio.sleep(1)
print("Maoliprints"+str(num)+"OK")

asyncdefmain(nums):
fornuminnums:
awaitprint_num(num)
%timeasyncio.run(main([iforiinrange(1,6)]))


Maoliisprinting1nows
Maoliprints1OK
Maoliisprinting2nows
Maoliprints2OK
Maoliisprinting3nows
Maoliprints3OK
Maoliisprinting4nows
Maoliprints4OK
Maoliisprinting5nows
Maoliprints5OK
Walltime:5.01s

asyncio.run() 函数用来运行最高层级的入口点 "main()" 函数

await 是同步调用等待一个协程。以下代码段会在等待 1 秒后打印 num,速度上没有发生改变。

需要引入asyncio.create_task才可以

可等待对象

如果一个对象可以在 await 语句中使用,那么它就是 可等待 对象

协程中的还一个重要概念,任务(Task)

如果写一个数字是一个任务,那么毛利我要完成5个任务

毛利我写个1-5都这么慢,不行,我要加速写

asyncio.create_task() 函数用来并发运行作为 asyncio 任务 的多个协程。

importasyncio

asyncdefprint_num(num):
print("Maoliisprinting"+str(num)+"nows")
awaitasyncio.sleep(1)
print("Maoliprints"+str(num)+"OK")

asyncdefmain(nums):
tasks=[asyncio.create_task(print_num(num))fornuminnums]
fortaskintasks:
awaittask
%timeasyncio.run(main([iforiinrange(1,6)]))


Maoliisprinting1nows
Maoliisprinting2nows
Maoliisprinting3nows
Maoliisprinting4nows
Maoliisprinting5nows
Maoliprints1OK
Maoliprints3OK
Maoliprints5OK
Maoliprints2OK
Maoliprints4OK
Walltime:1.01s

还可以写成await asyncio.gather(*tasks)这种方法

importasyncio

asyncdefprint_num(num):
print("Maoliisprinting"+str(num)+"nows")
awaitasyncio.sleep(1)
print("Maoliprints"+str(num)+"OK")

asyncdefmain(nums):
tasks=[asyncio.create_task(print_num(num))fornuminnums]
awaitasyncio.gather(*tasks)
%timeasyncio.run(main([iforiinrange(1,6)]))

*tasks 解包列表,将列表变成了函数的参数;与之对应的是, ** dict 将字典变成了函数的参数。

#asyncio 队列

asyncio也是只有在Pytohn3.7才有的东西。

asyncio 队列被设计成与 queue 模块类似。

importasyncio
importrandom

asyncdefconsumer(queue,id):
whileTrue:
val=awaitqueue.get()
print('{}getaval:{}'.format(id,val))
awaitasyncio.sleep(1)

asyncdefproducer(queue,id):
foriinrange(5):
val=random.randint(1,10)
awaitqueue.put(val)
print('{}putaval:{}'.format(id,val))
awaitasyncio.sleep(1)

asyncdefmain():
#创建队列
queue=asyncio.Queue()
#消费者1号
consumer_1=asyncio.create_task(consumer(queue,'consumer_1'))
#消费者2号
consumer_2=asyncio.create_task(consumer(queue,'consumer_2'))
#生产者1号
producer_1=asyncio.create_task(producer(queue,'producer_1'))
#生产者2号
producer_2=asyncio.create_task(producer(queue,'producer_2'))
#stop10秒
awaitasyncio.sleep(10)
consumer_1.cancel()
consumer_2.cancel()

awaitasyncio.gather(consumer_1,consumer_2,producer_1,producer_2,return_exceptions=True)

%timeasyncio.run(main())

协程的写法简洁清晰,只要把 async / await 语法和 create_task 结合来用,就是Python中比较常见的协程

如果不会多线程,看我之前的文章。

您可能还会对下面的文章感兴趣: