python之asyncio异步编程

异步编程可以这样来理解

# 伪代码
任务列表=[任务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():
    #这就是一个协程函数
    pass

result = 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 asyncio

async 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 tast1
    ret2 = await tast2
    print(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所有返回值全部拿到.

  

posted @ 2025-03-13 17:02  叶无齐  阅读(89)  评论(0)    收藏  举报

python学习之路

[python随笔]