本文介绍了Python如何使用
asyncio.create_task()
函数并发运行多个任务。
模拟长时间运行的操作
要模拟长时间运行的操作,可以使用asyncio
包的sleep()
协程。该sleep()
函数延迟指定的秒数:
<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python"><span style="color:#f47067">await</span> asyncio.sleep(seconds)</code></span></span>
因为sleep()
是协程,所以需要使用await
关键字。例如,下面使用sleep()
协程模拟 API 调用:
import asyncio |
|
async def call_api(message, result=1000, delay=3): |
|
print(message) |
|
await asyncio.sleep(delay) |
|
return result |
call_api()
是协程。它显示一条消息,暂停指定的秒数(默认为三秒),然后返回结果。
以下程序使用call_api()
两次并测量完成所需的时间:
import asyncio |
|
import time |
|
async def call_api(message, result=1000, delay=3): |
|
print(message) |
|
await asyncio.sleep(delay) |
|
return result |
|
async def main(): |
|
start = time.perf_counter() |
|
price = await call_api('Get stock price of GOOG...', 300) |
|
print(price) |
|
price = await call_api('Get stock price of APPL...', 400) |
|
print(price) |
|
end = time.perf_counter() |
|
print(f'It took {
round(end-start,0)} second(s) to complete.') |
|
asyncio.run(main()) |
输出:
Get stock price of GOOG... |
|
300 |
|
Get stock price of APPL... |
|
400 |
|
It took 6.0 second(s) to complete. |
首先,使用time
模块的perf_counter()
函数启动一个定时器来测量时间:
<span style="background-color:#f8f8f8"><span style="color:#212529"><code class="language-python"> start = time.perf_counter()</code></span></span>
二、调用call_api()
协程并显示结果:
price = await call_api('Get stock price of GOOG...', 300) |
|
print(price) |
三、call_api()
第二次调用:
price = await call_api('Get stock price of APPL...', 400) |
|
print(price) |
最后,显示程序完成所需的时间:
end = time.perf_counter() |
|
print(f'It took {
round(end-start,0)} second(s) to complete.') |
因为每个都call_api()
需要三秒钟,所以调用它两次需要六秒钟。
在这个例子中,我们直接调用了一个协程,并没有放到事件循环中去运行。相反,我们得到一个协程对象并使用await
关键字来执行它并得到一个结果。
我们使用async
andawait
编写异步代码但不能并发运行,要同时运行多个操作,我们需要使用称为任务的东西。
Python 任务简介
任务是协程的包装器,它安排协程尽快在事件循环上运行。
调度和执行以非阻塞方式发生,可以创建任务并在任务运行时立即执行其他代码。
请注意,任务不同于await
阻塞整个协程直到操作完成并产生结果的关键字。
重要的是可以创建多个任务并安排它们同时在事件循环中立即运行。
要创建任务,需要将协程传递给asyncio
包的create_task()
函数。该create_task()
函数返回一个Task
对象。
下面的程序说明了如何创建两个调度和执行call_api()
协程的任务:
import asyncio |
|
import time |
|
async def call_api(message, result=1000, delay=3): |
|
print(message) |
|
await asyncio.sleep(delay) |
|
return result |
|
async def main(): |
|
start = time.perf_counter() |
|
task_1 = asyncio.create_task( |
|
call_api('Get stock price of GOOG...', 300) |
|
) |
|
task_2 = asyncio.create_task( |
|
call_api('Get stock price of APPL...', 300) |
|
) |
|
price = await task_1 |
|
print(price) |
|
price = await task_2 |
|
print(price) |
|
end = time.perf_counter() |
|
print(f'It took {
round(end-start,0)} second(s) to complete.') |
|
asyncio.run(main()) |
输出:
Get stock price of GOOG... |
|
Get stock price of APPL... |
|
300 |
|
300 |
|
It took 3.0 second(s) to complete. |
在程序的某个时刻使用 await
关键字来等待任务是很重要的。
如果我们不使用 await
关键字,Python 会安排任务运行但在asyncio.run()
关闭事件循环时停止它。
下图说明了程序的执行流程:
最后,显示完成该main()
函数所需的时间:
end = time.perf_counter() |
|
print(f'It took {
round(end-start,0)} second(s) to complete.') |
通过使用该create_task()
函数,该程序要快得多。运行的任务越多,速度就越快。
等待时运行其他任务
当 call_api
运行时,可以运行其他任务。例如,以下程序在等待 call_api
任务时每秒显示一条消息:
import asyncio |
|
import time |
|
async def call_api(message, result=1000, delay=3): |
|
print(message) |
|
await asyncio.sleep(delay) |
|
return result |
|
async def show_message(): |
|
for _ in range(3): |
|
await asyncio.sleep(1) |
|
print('API call is in progress...') |
|
async def main(): |
|
start = time.perf_counter() |
|
message_task = asyncio.create_task( |
|
show_message() |
|
) |
|
task_1 = asyncio.create_task( |
|
call_api('Get stock price of GOOG...', 300) |
|
) |
|
task_2 = asyncio.create_task( |
|
call_api('Get stock price of APPL...', 300) |
|
) |
|
price = await task_1 |
|
print(price) |
|
price = await task_2 |
|
print(price) |
|
await message_task |
|
end = time.perf_counter() |
|
print(f'It took {
round(end-start,0)} second(s) to complete.') |
|
asyncio.run(main()) |
输出:
Get stock price of GOOG... |
|
Get stock price of APPL... |
|
API call is in progress... |
|
API call is in progress... |
|
API call is in progress... |
|
300 |
|
300 |
总结
- 任务是协程的包装器,它安排协程尽快在事件循环上运行。
- 使用
asyncio
库的create_task()
函数创建任务。 - 在程序中的某个时刻将
await
关键字与任务一起使用,以便可以在asyncio.run()
函数关闭事件循环之前完成任务。