python异步编程----asyncio 库协程理解

提示:异步编程,asyncio 库协程理解,可以与aiohttp或者httpx这种类型的库结合使用


前言

本人在学习过程中的参考资料如下:
参考资料:链接


一、Python 协程asyncio库理解

本质理解:本质上是单进程单线程,并不适合需要处理加快计算的任务,只适合处理那些需要等待的任务。最典型就是网络通讯。

1.1 示例1

首先我们需要建立一个概念:
event loop: 类似大脑的角色,用于决定运行哪一个任务。但是该大脑并不能自己主动停止当前正在运行任务,必须任务自己让出执行权。
coroutine:一般代指coroutine function 和 coroutine object。所有以 async def开头定义的函数都被称为 coroutine function,所有所有 coroutine function 被调用的时候都返回一个 coroutine object(具体见示例1的代码解释),并不会运行函数里面实际的程序,本质跟生成器函数有些像。

import asyncio

async def main():
    print('hello')
    await asyncio.sleep(1)
    print('world')

coro = main()  # 返回coroutine object,并不会实际运行函数里面的程序 

运行示例:
在这里插入图片描述

1.2 示例2

如何执行coroutine function呢?

  1. 假设我们正常代码是工作在synchronize模式。需要进入async模式,即进入event loop.
  2. 使用await 和 asyncio.run
import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep(delay)
    print(what)

async def main():
    print(f"started at {time.strftime('%X')}")
    '''
        await 会将say_after(1, 'hello')任务注册, 并被告知event loop
        现在event loop知道了这个task, 当前main这个task会告知event loop, 需要say_after(1, 'hello')这个task完成之后才能继续执行
        main()这个task会被yield出去,event loop会去执行其他task,即say_after(1, 'hello')这个task
        
        如果有返回值,可以按照像其他函数那样去接收该返回值即可
    '''
    await say_after(1, 'hello') 
    '''
    	say_after(2, 'world')执行流程与say_after(1, 'hello') 相同
    '''
    await say_after(2, 'world')
    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

运行示例:
在这里插入图片描述
从结果输出可以观察到,总共用时三秒。
详细执行顺序

  1. 启动程序和打印开始时间:
    (1)main() 函数开始执行。
    (2)打印当前时间,这是程序开始的标记,输出 “started at 20:36:50”(具体时间依系统而定)。
  2. 执行第一个异步任务 say_after(1, ‘hello’):
    (1)await say_after(1, ‘hello’) 这行代码调用 say_after 函数,并告诉事件循环,main() 函数在这个异步任务完成之前不会继续执行。
    (2)say_after(1, ‘hello’) 开始执行,它首先使当前任务(即 say_after 的执行)在事件循环中暂停执行1秒(await asyncio.sleep(1))。在这1秒内,事件循环可以执行其他任务(如果有的话),因为后面这个异步任务还没有被注册,因此还不能被运行,所以在我们的例子中,没有其他并发任务,所以事件循环只是等待这个延迟结束。
    (3)1秒后,say_after(1, ‘hello’) 唤醒并打印 “hello”。
  3. 执行第二个异步任务 say_after(2, ‘world’):
    (1)一旦第一个 say_after 完成,await say_after(2, ‘world’) 开始执行。这与第一个调用类似:main() 函数在此任务完成之前暂停,say_after(2, ‘world’) 开始执行并立即进入2秒的暂停状态。
    (2)2秒后,say_after(2, ‘world’) 唤醒并打印 “world”。
  4. 打印结束时间并完成主函数:
    (1)两个 say_after 调用依次完成后,main() 函数恢复执行。
    (2)打印当前时间作为结束的标记,输出 “finished at 20:36:53”。
    其他写法
import asyncio
import time
async def say_after(delay, what):
    await asyncio.sleep( delay)
    print(what)

async def main():
    task1 = asyncio.create_task(
    say_after(1, 'hello'))

    task2 = asyncio.create_task(
    say_after(2, 'world'))

    print(f"started at {time.strftime('%X')}")
    
    # 当wait后面不是coroutine的时候,会将将task注册到event loop。
    await task1  # await会让当前任务交出控制权,然后由event loop去选择其他task执行,并获得任务返回值
    await task2
    print(f"finished at {time.strftime('%X')}")

asyncio.run(main())

在这里插入图片描述
请注意到,我们这里运行只用了2秒钟
执行顺序详解

  1. 定义并启动任务:
    (1)task1 和 task2 通过 asyncio.create_task() 被创建并注册到事件循环中。这意味着它们准备被执行,并且一旦事件循环开始运行,它们就会根据调度开始执行。
    (2)task1 被设置为延迟1秒后打印 “hello”。
    (3)task2 被设置为延迟2秒后打印 “world”。
  2. 任务开始执行:
    (1)程序流到达 await task1。此时 task1 开始执行,因为其延迟是1秒,所以它进入等待状态。在这个等待期间(await asyncio.sleep(delay)),控制权被交还给事件循环。
  3. 事件循环处理任务:
    (1)由于 task1 正在等待,事件循环检查是否有其他任务可以执行。此时,task2 也开始执行,并因为同样的原因(即 await asyncio.sleep(delay))进入等待状态。
    (2)在这两个任务都处于睡眠状态的时候,事件循环会空闲等待直到下一个任务准备好执行。
  4. 任务唤醒和继续执行:
    (3)task1 的等待时间(1秒)首先完成,所以 “hello” 被打印出来。完成后,控制权再次回到 main() 函数,接着执行 await task2。
    (4)由于 task2 的等待时间设置为2秒,当 task1 完成时,它还在睡眠中。因此,main() 函数会在 await task2 处继续等待,直到 task2 也醒来并打印 “world”。
  5. 完成所有任务:
    (1)当两个任务都完成后,main() 函数打印 “finished at”,标记结束时间。

1.3 示例3

获取返回值

import asyncio
import time
async def say_after(delay, what):
    await asyncio.sleep( delay)
    return f"{what} - {delay}"

async def main():
    task1 = asyncio.create_task(
    say_after(1, 'hello' ))

    task2 = asyncio.create_task(
    say_after(2, 'world' ))

    print(f"started at {time . strftime('%X' )}")
   	
    ret1 = await task1
    ret2 = await task2

    print(ret1)
    print(ret2)
    print(f"finished at {time . strftime('%X' )}")

asyncio.run(main())

1.4 示例4

使用gather函数将多个task组合起来一次执行

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep( delay)
    return f"{what} - {delay}"

async def main():
    task1 = asyncio.create_task(
    say_after(1, 'hello' ))

    task2 = asyncio.create_task(
    say_after(2, 'world' ))

    print(f"started at {time . strftime('%X' )}")
    
    """当含有多个任务时,可以使用gather将多个task组合起来一次执行,返回值则是按照task对应在gather中的顺序,以列表形式返回
        gather会返回一个future
    """
    ret = await asyncio.gather(task1, task2)

    print(ret)
    
    print(f"finished at {time . strftime('%X' )}")

asyncio.run(main())

运行结果:
在这里插入图片描述
使用gather函数,也可以直接将coroutine function丢进gather

import asyncio
import time

async def say_after(delay, what):
    await asyncio.sleep( delay)
    return f"{what} - {delay}"

async def main():

    print(f"started at {time . strftime('%X')}")
    
    """
        gather中也可以直接把coroutine 直接扔进,会自动转换为task
    """
    ret = await asyncio.gather(say_after(1, 'hello'), say_after(2, 'world'))

    print(ret)
    print(f"finished at {time . strftime('%X')}")

asyncio.run(main())

运行结果:
在这里插入图片描述

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值