Python中的协程
定义一个协程
import asyncio
async def execute(x):
print(x)
coroutine = execute(1)
print('coroutine:',coroutine)
loop = asyncio.get_event_loop()
loop.run_until_complete(coroutine)
运行结果
coroutine: <coroutine object execute at 0x10d675048>
1
- 第一行:导入
asyncio
这个包,才能使用async
和await
。 - 第二行:使用
async
定义一个execute()
方法。 - 第四行:直接调用该方法,但是方法没有执行,而是返回一个
coroutine
协程对象。 - 第五行:打印出来
coroutine
协程对象。 - 第六行:
asyncio.get_event_loop
创建一个事件循环loop
。 - 第七行:调用
loop
对象的run_until_complete()
方法将协程注册到事件循环loop
中,然后启动,才可以执行execute()
方法打印输出结果。
async
定义的方法会变成一个无法直接执行的coroutine
对象,要将其注册到事件循环中才能执行。
定义第二个协程
第一种定义task对象方式
**task:**任务,是对协程对象(coroutine)的进一步封装,包含任务的各个状态,相比coroutine
对象多了运行状态,如running、funished
等。
import asyncio
async def execute(x):
print(x)
return x
coroutine = execute(1)
print('coroutine:',coroutine)
loop = asyncio.get_event_loop()
task = loop.create_task(coroutine)
print('Task:',task)
loop.run_until_complete(task)
print('Task:',task)
运行结果
coroutine: <coroutine object execute at 0x1048fa048>
Task: <Task pending coro=<execute() running at /Users/hubo/Downloads/untitled4.py:2>>
1
Task: <Task finished coro=<execute() done, defined at /Users/hubo/Downloads/untitled4.py:2> result=1>
我们将coroutine
对象传递给run_until_complete
方法时,实际上它进行了一个操作就是将coroutine
封装成task
对象。
- 第八行:调用
loop
对象的create_task()
方法,将coroutine
对象转化成task
对象。 - 第九行:输出的时候发现
task
是pending
状态。 - 第十行:我们将
task
对象添加到事件循环中执行。 - 第十一行:输出
task
对象的时候,发现状态变成了finished
,同时result
变成了1,也就是execute
方法返回结果。
第二种定义task对象方式
import asyncio
async def execute(x):
print(x)
return x
coroutine = execute(2)
print('coroutine:',coroutine)
task = asyncio.ensure_future(coroutine)
loop = asyncio.get_event_loop()
print('Task:',task)
loop.run_until_complete(task)
print('Task:',task)
运行结果
coroutine: <coroutine object execute at 0x10dfdc048>
Task: <Task pending coro=<execute() running at /Users/hubo/Downloads/untitled4.py:2>>
2
Task: <Task finished coro=<execute() done, defined at /Users/hubo/Downloads/untitled4.py:2> result=2>
直接通过asyncio
的ensure_future()
方法,返回结果就是task
对象,我们就不用借助于loop
来定义。即使没有声明loop
也可以定义task
对象。
为Task绑定一个回调方法
import asyncio
import requests
async def request():
url = 'http://baidu.com'
status = requests.get(url)
return status
def callback(task):
print('Status:',task.result())
coroutine = request()
task = asyncio.ensure_future(coroutine)
task.add_done_callback(callback)
print('Task:',task)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)
print('Task:',task)
- 第11行:我们将
callback()
方法传递给了封装好的task
对象,这样task执行完毕后,就可以调用callback()
方法,同时task
对象还会做为参数传递给callback()
方法,调用task
对象的result()
方法获取返回结果。
多任务协程
上面的例子只执行了一次请求,当执行多次请求应该怎么办?定义一个task
列表,然后使用asyncio
的wait()
方法就即可执行。
import asyncio
import requests
async def request():
url = 'http://baidu.com'
status = requests.get(url)
return status
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
print(tasks)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
for task in tasks:
print('Task Result:',task.result())
运行结果
[<Task pending coro=<request() running at /Users/hubo/Downloads/untitled4.py:3>>, <Task pending coro=<request() running at /Users/hubo/Downloads/untitled4.py:3>>, <Task pending coro=<request() running at /Users/hubo/Downloads/untitled4.py:3>>, <Task pending coro=<request() running at /Users/hubo/Downloads/untitled4.py:3>>, <Task pending coro=<request() running at /Users/hubo/Downloads/untitled4.py:3>>]
Task Result: <Response [200]>
Task Result: <Response [200]>
Task Result: <Response [200]>
Task Result: <Response [200]>
Task Result: <Response [200]>
- 第7行:使用
for
循环创建五个task
,组成一个列表。 - 第10行:把列表首先传递给了
asyncio
的wait()
方法,然后再将其注册到时间循环中,就可以发起五个任务了。 - 第11行:将结果输出。
使用aiohttp
aiohttp 是一个支持异步请求的库,利用它和 asyncio 配合我们可以非常方便地实现异步请求操作。
import asyncio
import requests
import aiohttp
async def request():
url = 'http://httpbin.org'
session = aiohttp.ClientSession()
response = await session.get(url)
session.close()
print(response)
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
**注意:**将请求库由 requests 改成了 aiohttp,通过 aiohttp 的 ClientSession 类的 get() 方法进行请求。
await
:遇到非阻塞的会继续执行,遇到阻塞的耗时请求会被挂起,会执行另一个task
,当全部的task
挂起后,就会继续等待。