目录
(1)event_loop&asyncio.wait | asyncio.gather
(2)asyncio.run&asyncio.wait | asyncio.gather
(3)asyncio.run&asyncio.as_complete
一、什么是协程
协程(Coroutine),也可以被称为微线程,也称为用户级线程,是一种用户态内的上下文切换技术。在不开辟新线程的基础上完成多任务,其实就是通过一个线程实现代码间相互切换执行,在一个线程(协程)中,遇到io等待时间,线程可以利用这个等待时间去做其他事情。
并发的本质:切换+保存状态
cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长或有一个优先级更高的程序替代了它。
在Python中有多种方式可以实现协程,如下:
-
yield:生成器,借助生成器的特点可以实现协程代码;
-
greenlet:是一个第三方模块,用于实现协程代码,需要手动切换;
-
gevent:基于greenlet实现的,遇到IO操作能够自动切换任务,等待IO操作完成,再在适当的时候切换回来继续执行;
-
asyncio:在Python3.4中引入的模块用于编写协程代码;
-
async & awiat:在Python3.5中引入的两个关键字,结合asyncio模块可以更方便的编写协程代码。
二、协程实现方式
1. yield实现
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import time
# yield 实现协程
# 协程之间执行任务按照一定顺序交替执行
def task1():
for i in range(5):
print("---task1---", i)
time.sleep(0.5)
yield
def task2():
for i in range(5):
print("---task2---", i)
time.sleep(0.5)
yield
def main():
t1 = task1()
t2 = task2()
while True:
try:
next(t1)
next(t2)
except StopIteration:
break
print("main finsh...")
if __name__ == '__main__':
main()
运行结果:
---task1--- 0
---task2--- 0
---task1--- 1
---task2--- 1
---task1--- 2
---task2--- 2
---task1--- 3
---task2--- 3
---task1--- 4
---task2--- 4
main finsh...
2. greenlet实现
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import time
from greenlet import greenlet
def task1(num=5):
for i in range(num):
print("tak1----", i)
# time.sleep(1)
g2.switch()
def task2(num=5):
for i in range(num):
print("tak2----", i)
time.sleep(1)
g1.switch()
g1 = greenlet(task1)
g2 = greenlet(task2)
g1.switch()
输出结果:
tak1---- 0
tak2---- 0
tak1---- 1
tak2---- 1
tak1---- 2
tak2---- 2
tak1---- 3
tak2---- 3
tak1---- 4
tak2---- 4
3. gevent实现
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import gevent
import time
def task(name, num):
for i in range(num):
print(gevent.getcurrent(), name, i)
# 模拟一个耗时操作,注意不能使用time模块的sleep
# time.sleep(1) # 顺序执行
gevent.sleep(1) # 模拟耗时,遇到io操作切换到其他协程执行
def main():
# spawn 方式创建协程
g1 = gevent.spawn(task, 'task1...', 9)
g2 = gevent.spawn(task, 'task2...', 5)
g3 = gevent.spawn(task, 'task3...', 3)
# d等待协程运行
# g1.join()
# g2.join()
# g3.join()
gevent.joinall([
g1, g2, g3
])
if __name__ == '__main__':
main()
运行结果如下:
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 0
<Greenlet at 0x19a14338e00: task('task2...', 5)> task2... 0
<Greenlet at 0x19a1433d120: task('task3...', 3)> task3... 0
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 1
<Greenlet at 0x19a14338e00: task('task2...', 5)> task2... 1
<Greenlet at 0x19a1433d120: task('task3...', 3)> task3... 1
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 2
<Greenlet at 0x19a14338e00: task('task2...', 5)> task2... 2
<Greenlet at 0x19a1433d120: task('task3...', 3)> task3... 2
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 3
<Greenlet at 0x19a14338e00: task('task2...', 5)> task2... 3
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 4
<Greenlet at 0x19a14338e00: task('task2...', 5)> task2... 4
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 5
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 6
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 7
<Greenlet at 0x19a1419c900: task('task1...', 9)> task1... 8
4. gevent-monkey补丁
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import gevent
import time
from gevent import monkey
monkey.patch_all()
def task(name, num):
for i in range(num):
print(gevent.getcurrent(), name, i)
# 模拟一个耗时操作,注意不能使用time模块的sleep
time.sleep(1) # 顺序执行
def main():
gevent.joinall([
gevent.spawn(task, 'task1...', 5),
gevent.spawn(task, 'task2...', 5),
gevent.spawn(task, 'task3...', 3)
])
print("main finish...")
if __name__ == '__main__':
main()
运行结果:
<Greenlet at 0x22011992020: task('task1...', 5)> task1... 0
<Greenlet at 0x220119fd800: task('task2...', 5)> task2... 0
<Greenlet at 0x220119fd8a0: task('task3...', 3)> task3... 0
<Greenlet at 0x22011992020: task('task1...', 5)> task1... 1
<Greenlet at 0x220119fd800: task('task2...', 5)> task2... 1
<Greenlet at 0x220119fd8a0: task('task3...', 3)> task3... 1
<Greenlet at 0x22011992020: task('task1...', 5)> task1... 2
<Greenlet at 0x220119fd800: task('task2...', 5)> task2... 2
<Greenlet at 0x220119fd8a0: task('task3...', 3)> task3... 2
<Greenlet at 0x22011992020: task('task1...', 5)> task1... 3
<Greenlet at 0x220119fd800: task('task2...', 5)> task2... 3
<Greenlet at 0x22011992020: task('task1...', 5)> task1... 4
<Greenlet at 0x220119fd800: task('task2...', 5)> task2... 4
main finish...
5. asyncio装饰器
import asyncio
@asyncio.coroutine
def task1():
print(1)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
@asyncio.coroutine
def task2():
print(3)
yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(4)
tasks = [
asyncio.ensure_future(task1()),
asyncio.ensure_future(task2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
运行结果:
1
3
2
4
6. async&await关键字
import asyncio
async def task1():
for i in range(5):
print("---task1---", i)
await asyncio.sleep(0.5)
async def task2():
for i in range(5):
print("---task1---", i)
await asyncio.sleep(0.5)
def main():
loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(task1()),
asyncio.ensure_future(task2())
]
loop.run_until_complete(asyncio.wait(tasks))
if __name__ == '__main__':
main()
运行结果:
---task1--- 0
---task1