协程(Coroutine),也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行
协程意义:在一个线程中如果遇到IO等待时间,线程不会傻傻等,利用空闲的时候再去干点其他事。
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
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的效率有多高
事件循环就是把你创建好的函数,加入到任务列表,事件循环负责监听任务列表,当执行这个函数遇到费时操作时,事件循环就会控制跳到其他可执行的任务,当没有可执行的任务就会等待当前任务执行完毕,只有全部任务都执行完毕,才会退出循环。
# 伪代码 任务列表 = [ 任务1, 任务2, 任务3,... ] while True: 可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回 for 就绪任务 in 可执行的任务列表: 执行已就绪的任务 for 已完成的任务 in 已完成的任务列表: 在任务列表中移除 已完成的任务 如果 任务列表 中的任务都已完成,则终止循环
import asyncio # 去生成或获取一个事件循环 loop = asyncio.get_event_loop() # 将任务放到`任务列表` loop.run_until_complete(任务)
控制任务
通过asyncio.wait()可以控制多任务
asyncio.wait()是一个协程,不会阻塞,立即返回,返回的是协程对象。传入的参数是future或协程构成的可迭代对象。最后将返回值传给run_until_complete()加入事件循环
协程函数,定义函数的时候使用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
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))
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)