异步编程可以这样来理解
# 伪代码
任务列表=[任务1,任务2,任务3,..
while True:
可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已 完成"的任务返回
for 就绪任务 in 可执行的任务列表:
执行已就绪的任务
for已完成的任务 in 已完成的任务列表:
在任务列表中移除 已完成的任务
如果 任务列表 中的任务都已完成,则终止循环
大概的代码如下:
import asyncio #生成或者获取一个事件循环 loop = asyncio.get_event_loop() #将任务房到 任务列表 中 loop.run_until_complete(任务)
协程函数,定义函数时 async def 函数名(): 这样的函数就是携程函数
协程对象,执行 协程函数 就得到了协程对象.
async def test():#这就是一个协程函数passresult = test() #这就是一个协程对象
需要注意的是:执行协程函数创建协程对象时,函数里的代码并不会像普通执行函数一样,立刻就执行.而是需要这样来执行
async def test():#这就是一个协程函数print("快来吧!")result = test() #这就是一个协程对象 loop = asyncio.get_event_loop() loop.run_until_complete(result)
在python3.7之前,我们一般通过
loop = asyncio.get_event_loop() loop.run_until_complete(result)
来执行协程对象
但是呢,python3.7以后有了更方便的方法 asyncio.run(任务)
import asyncioasync def test():#这就是一个协程函数print("快来吧!")result = test() #这就是一个协程对象 asyncio.run(result) # 运行协程对象
下面我们来看await ,await就是等待对象得到值以后,再继续往下走下去,我们看代码来分析
import asyncio async def other():print('切入了other')await asyncio.sleep(2)print('切走了other')return '从other返回的结果'async def test():print('切入了test')ret = await other()print('test执行完毕',ret)asyncio.run(test()) ''' 切入了test 切入了other 切走了other test执行完毕 从other返回的结果 '''
可以看出当test函数被执行的时候,他先切入了test,随后ret调用了other函数,等other函数执行完毕后,再继续执行了test函数剩余的内容.
我们来让other函数连续执行两次.
import asyncio async def other():print('切入了other')await asyncio.sleep(2)print('切走了other')return '从other返回的结果'async def test():print('切入了test')ret = await other()print('test执行完毕',ret)ret2 = await other()print('test执行完毕',ret2)asyncio.run(test()) ''' 切入了test 切入了other 切走了other test执行完毕 从other返回的结果 切入了other 切走了other test执行完毕 从other返回的结果 '''
可以看出等第一次other函数执行完,才执行的第二次other函数,程序并没有在两个other之间跳跃,也就是说,如果执行到了await,他会耐心的等待程序执行完毕,有了结果,才继续往下执行.不要觉得await好像什么用都没有,实际上他的用处也很多的.后面我们会再讲.
下面我们来看 Task对象 task对象是用来并发调度协程,可以添加多个task对象用来调度.
import asyncio async def other():print('切入了other')await asyncio.sleep(2)print('切走了other')return '从other返回的结果' async def test():print('切入了test')# 创建两个task对象tast1 = asyncio.create_task(other())tast2 = asyncio.create_task(other())print('test执行完毕')ret1 = await tast1ret2 = await tast2print(ret1,ret2)asyncio.run(test()) ''' 切入了test test执行完毕 切入了other 切入了other 切走了other 切走了other 从other返回的结果 从other返回的结果 '''
我们来看结果,首先切入了test,然后直接往下执行了test执行完毕,然后连续切入两次other,然后连续切出other.最后拿到了两个返回的结果.
后面两个await的作用是,当test协程执行完之前,让他等待这两个other执行结束,否则程序会直接跑完test然后就结束.我们很可能等不到other执行完毕.
针对task我们其实还有更方便的写法.
import asyncio async def other():print('切入了other')await asyncio.sleep(2)print('切走了other')return '从other返回的结果' async def test():print('切入了test')tasklist = [asyncio.create_task(other()),asyncio.create_task(other()),asyncio.create_task(other())]print('test执行完毕')ret = await asyncio.wait(tasklist)print(ret)asyncio.run(test())''' 切入了test test执行完毕 切入了other 切入了other 切入了other 切走了other 切走了other 切走了other ({<Task finished name='Task-4' coro=<other() done, defined at g:\vscodefile\python\test\ostest.py:2> result='从other返回的结果'>, <Task finished name='Task-3' coro=<other() done, defined at g:\vscodefile\python\test\ostest.py:2> result='从other返回的结果'>, <Task finished name='Task-2' coro=<other() done, defined at g:\vscodefile\python\test\ostest.py:2> result='从other返回的结果'>}, set()) '''
补充三点:第一 可以给每个task对象命名
tasklist = [asyncio.create_task(other(),name='n1'),asyncio.create_task(other(),name='n2'),asyncio.create_task(other(),name='n3'),] #这样做的好处是,在拿返回值的时候({<Task finished name='Task-4' coro=<other() done, defined at g:\vscodefile\python\test\ostest.py:2> result='从other返回的结果'>, <Task finished name='Task-3' coro=<other() done, defined at g:\vscodefile\python\test\ostest.py:2> result='从other返回的结果'>, <Task finished name='Task-2' coro=<other() done, defined at g:\vscodefile\python\test\ostest.py:2> result='从other返回的结果'>}, set()) 集合总返回的name不再是task-4 taks-3 而是你命名的,这样比较好拿结果
另外一点是当协程调度时,我们可以设定超时的时间
ret,penging = await asyncio.wait(tasklist,timeout=5) #设置了timeout 我们需要用两个变量来接收调度返回值,ret是运行完的协程返回值,而pengding是未运行完的协程返回的值
最后 我们其实更倾向于用 ret = await asyncio.gather()来并发等待所有task结束
done = await asyncio.gather(*tasklist)#首先,这里得到的是一个列表 #['从other返回的结果', '从other返回的结果', '从other返回的结果'] #这样更方便我们拿返回值. #其次,这里可以直接把task放进来执行,也可以直接把futere放进来执行,他会一次先把所有的futere转成task,交给event loop去调度执行,最后才await所有返回值全部拿到.