asyncio
协程
协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行
协程意义:在一个线程中如果遇到IO等待时间,线程不会傻傻等,利用空闲的时候再去干点其他事。
一、效果演示
1.1 正常执行
from time import sleep
from time import time
start = time()
def func1():
print(1)
sleep(1)
print(2)
def func2():
print(3)
sleep(1)
print(4)
func1()
func2()
end = time() # 输出1234
print(end-start) # 2.028127908706665
1.2 使用asynico
import asyncio
from time import time
start = time()
async def func1():
print(1)
await asyncio.sleep(1)
print(2)
async def func2():
print(3)
await asyncio.sleep(1)
print(4)
tasks = [
func1(),
func2()
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time() # 输出 1324
print(end-start) # 1.010634422302246
可以很明显的看到程序在遇到i0费时操作时,asyncio的效率有多高
学习使用
3.1 事件循环
事件循环就是把你创建好的函数,加入到任务列表,事件循环负责监听任务列表,当执行这个函数遇到费时操作时,事件循环就会控制跳到其他可执行的任务,当没有可执行的任务就会等待当前任务执行完毕,只有全部任务都执行完毕,才会退出循环。
# 伪代码
任务列表 = [ 任务1, 任务2, 任务3,... ]
while True:
可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
for 就绪任务 in 可执行的任务列表:
执行已就绪的任务
for 已完成的任务 in 已完成的任务列表:
在任务列表中移除 已完成的任务
如果 任务列表 中的任务都已完成,则终止循环
import asyncio
# 去生成或获取一个事件循环
loop = asyncio.get_event_loop()
# 将任务放到`任务列表`
loop.run_until_complete(任务)
asyncio.wait()
控制任务
通过asyncio.wait()可以控制多任务
asyncio.wait()是一个协程,不会阻塞,立即返回,返回的是协程对象。传入的参数是future或协程构成的可迭代对象。最后将返回值传给run_until_complete()加入事件循环
3.2 携程函数
协程函数,定义函数的时候使用async def 函数名
协程对象,执行写成函数()得到的学成对象
# d定义协程函数
async def func():
pass
# 获取写成对象
result = func()
print(result) # <coroutine object func at 0x000002326168AD40>
注意:执行协程函数创建协程对象,函数内部代码不会执行。
如果想要运行协程函数内部代码,必须要讲协程对象交给事件循环来处理。
import asyncio
async def func():
print("执行协程函数!")
result = func()
loop = asyncio.get_event_loop()
loop.run_until_complete(result)
python3.7有个更简便的方法
import asyncio
async def func():
print("执行协程函数!")
result = func()
# loop = asyncio.get_event_loop()
# loop.run_until_complete( result )
asyncio.run(result) # python3.7
3.3 await
await + 可等待的对象(协程对象、Future、Task对象 -> IO等待)
示例一
import asyncio
async def func():
print("开始")
response = await asyncio.sleep(2)
print("结束",response)
asyncio.run( func() )
示例二:
import asyncio
async def others():
print("start")
await asyncio.sleep(2)
print('end')
return '返回值'
async def func():
print("执行协程函数内部代码")
# 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
response = await others()
print("IO请求结束,结果为:", response)
async def func1():
print('func1')
tasks = [
func(),
func1()
]
asyncio.run(asyncio.wait(tasks))
3.4 Task对象
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)
的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task()
函数以外,还可以用低层级的 loop.create_task()
或 ensure_future()
函数。不建议手动实例化 Task 对象。
注意:asyncio.create_task()
函数在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future()
函数。
示例一:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
async def main():
print("main开始")
# 创建Task对象,将当前执行func函数任务添加到事件循环。
task1 = asyncio.create_task( func() )
# 创建Task对象,将当前执行func函数任务添加到事件循环。
task2 = asyncio.create_task( func() )
print("main结束")
# 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
# 此处的await是等待相对应的协程全都执行完毕并获取结果
ret1 = await task1
ret2 = await task2
print(ret1, ret2)
asyncio.run( main() )
示例二:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
async def main():
print("main开始")
# name给任务起个名字
task_list = [
asyncio.create_task(func(), name='n1'),
asyncio.create_task(func(), name='n2')
]
print("main结束")
# done是任务列表返回的集合,pending未完成任务的集合
done, pending = await asyncio.wait(task_list, timeout=None)
print(done)
# {<Task finished name='n1' coro=<func() done, defined at C:/Users/86166/Deskto/a.py:4> result='返回值'>, <Task finished name='n2' coro=<func() done, defined at C:/Users/86166/Desktop/a.py:4> result='返回值'>}
asyncio.run(main())
示例三:
import asyncio
async def func():
print(1)
await asyncio.sleep(2)
print(2)
return "返回值"
task_list = [
func(),
func(),
]
done,pending = asyncio.run( asyncio.wait(task_list) )
print(done)