协程

python协程

背景:

  当发送一个网络请求时,受限于网络环境影响可能需要等待一段时间,但此时cpu是空闲的,此时可以利用cpu处理另外的运算,直到收到服务端的回应继续运算。协程就是处理这种情形的,因此协程需要底层的支持。

原理:

  这部分是个人对于协程原理的理解,等执行任务A到达一个不需要cpu但耗时的操作时,可以将A当前状态保存,并返回一个特殊的对象a,这个对象在A的操作完成时发送一个信号使得A可以继续,由于A此时返回了a,释放了当前资源,任务B就可以运行,任务A,B可以理解为task,而对象a可以理解为furture。

实践:

1.多次请求同一url

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import aiohttp
import asyncio
import datetime

async def test(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
print(resp.status, datetime.datetime.now())

async def main():
for i in range(10):
print(i)
asyncio.create_task(test("https://google.com"))

loop = asyncio.get_event_loop()
loop.create_task(main())
loop.run_forever()

  该例子会输出0~9,说明等待响应并没有阻碍下一次请求。需要注意await是让出运行权限,并不会创建task,以下写法不会产生异步的效果

1
2
3
4
async def main():
for i in range(10):
print(i)
await asyncio.create_task(test("https://google.com"))

await不会将task放入循环,反而会让出权限等待task执行完毕

2.占有权限

1
2
3
4
5
6
7
8
9
10
11
12
13
async def a():
print("a start")
await asyncio.sleep(1)
print("a end")

async def b():
print("b start")
time.sleep(3)
print("b end")

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([a(), b()]))

  以下是结果,可以看出a让出使用权限后,即使furture完成,也只能等待b让出权限后才能执行。

1
2
3
4
a start
b start
b end
a end

并且更换a,b的顺序

1
loop.run_until_complete(asyncio.wait([b(), a()]))

会使协程变成串行失去意义,可以猜想实际loop中有两个队列,一个为task队列,另一个为信号队列,用于指示当前task让出权限后执行的task。