异步的用法

Hello,大家好。本期来和大家一起学习一下异步的相关知识。

基础知识补充

同步和异步的区别

同步是阻塞模式,异步是非阻塞模式

同步:一个程序发出某个请求时,若该请求需要一段时间才能返回响应。那么这个程序会一直等待下去,直到接收到响应信息才会执行下面的操作。

异步:一个程序发出某个请求时,若该请求需要一段时间才能返回响应。则该程序不会等待,而是继续执行下面的操作。当接收到响应信息时才会返回来处理。

同步和异步的优缺点:

  • 1、同步的执行效率会比较低,耗费时间,但有利于我们对流程进行控制,避免很多不可掌控的意外情况;

  • 2、异步的执行效率高,节省时间,但是会占用更多的资源,也不利于我们对进程进行控制;

异步实现

首先我们要知道异步的几个组件。

协程函数和协程对象

协程函数:在定义函数时使用 async 关键字修饰的函数被称之为协程函数。

协程函数的特殊之处:

  • 协程函数调用后,函数内部的代码不会被【立即】执行。

  • 协程函数调用后,返回值是一个协程对象。

协程对象:执行 协程函数() 得到的返回值就是协程对象。

# 定义协程函数
async def func():
    print('这是一个协程任务')

# 获取协程对象
result = func()

想要执行协程函数,需要协程函数,协程对象,事件循环(下面讲解)搭配使用。

import asyncio

# 定义协程函数
async def func():
    print('这是一个协程任务')

# 1.获取协程对象
result = func()

# 2.创建事件循环
look = asyncio.get_event_loop()

# 3.将协程对象放入事件循环中并运行
look.run_until_complete(result)

'''
运行结果:
这是一个协程任务
'''

在python3.7之后无需自己创建事件循环,可以直接使用 asyncio.run(任务) 自动创建事件循环并允许。

import asyncio


# 定义协程函数
async def func():
    print('这是一个协程任务')


# 1.获取协程对象
result = func()

# 2.使用asyncio中的run方法自动创建事件循环
asyncio.run(result)

'''
运行结果:
这是一个协程任务
'''

await关键字

定义:等待某些IO任务

作用:await是一个只能在协程中使用的关键字,用于遇到IO操作时,挂起当前协程任务。当协程任务挂起后,事件循环去执行其他协程任务。当IO操作完成时,事件循环再次切换回来执行await之后的代码。

使用方式:await + 可等待对象

示例1:

import asyncio


async def func():
    print("开始协程任务...")

    # 模拟网络IO请求。如果事件循环中有其他协程任务,则自动切换。
    # await 如果没有接收到后面代码的返回值,则一直阻塞直到有返回值返回。
    response = await asyncio.sleep(2)

    print("IO请求结束,结果为:", response)


asyncio.run(func())
'''
运行结果:
开始协程任务...
IO请求结束,结果为:None
'''

如果多个协程函数是如何运行的呢?

示例2:

import asyncio


async def others():
    print("start")
    await asyncio.sleep(2)
    print('end')
    return '返回值'


async def func():
    print("执行协程函数内部代码")
    # 可以通过await同步切换执行其他协程函数
    # await 本质就是等待
    response_1 = await others()
    print(response_1)

    response_2 = await others()
    print(response_2)


asyncio.run(func())
'''
运行结果:
执行协程函数内部代码
start
end
返回值
start
end
返回值
'''
可以发现,执行完第一次others()之后才会执行第二次。
当前代码本质还是一个同步单线程代码。
这是为什么呢?
原因:若事件循环传入的是一个普通的协程对象,则执行方式为同步。若传入的是task(任务)对象,则执行方式为异步。

task(任务)对象

本质:就是一个高级的协程对象。(可以显示协程的状态)

与协程对象的不同点:

1.任务对象可以绑定回调函数,协程对象不能绑定回调函数。

2.任务对象是并发,协程对象是同步。

使用方式:

# 创建方式1.更加底层方法
task = asyncio.ensure_future(协程对象)
# 创建方式2.只支持python3.7及以上
#task = asyncio.create_task(协程对象)

实例1:

import asyncio


async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


async def main():
    print("开始任务")

    # 创建task对象
    task1 = asyncio.create_task(func())
    task2 = asyncio.create_task(func())
    print("结束任务")

    # 如果await执行的是一个task对象,那么事件循环的执行方式为异步。
    # 遇到await进行任务切换
        # 如果是协程对象则是同步切换
        # 如果是task对象则是异步切换
    res1 = await task1
    res2 = await task2
    print(res1, res2)


asyncio.run(main())
'''
运行结果:
开始任务
结束任务
1
1
2
2
返回值 返回值
'''

弊端:如果协程对象很多,一个一个写很麻烦。

实例2:

import asyncio


async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


async def main():
    print("开始任务")
    # 将协程封装到Task对象中并添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
    tasks = [
        asyncio.create_task(func()),
        asyncio.create_task(func())
    ]
    # 如果想要运行列表中的任务,需要将列表对象转为一个可以等待的对象
    # wait方法返回一个元组(函数返回值,任务状态)
    # 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中。
    done, pending = await asyncio.wait(tasks, timeout=None)
    # 如果现在想要获取当前协程中的返回值怎么办?
    for item in done:
        print(item.result())


asyncio.run(main())
'''
运行结果:
开始任务
1
1
2
2
返回值
返回值
'''

协程任务列表也可以写在函数外。

实例3:

import asyncio


async def func():
    print(1)
    await asyncio.sleep(2)
    print(2)
    return "返回值"


print("开始任务")
# 错误:tasks = [ asyncio.create_task(func()), asyncio.create_task(func())]
tasks = [
    func(),
    func()
]
# task对象创建时必须要保证事件循环已经存在
# asyncio.wait内部会对列表中的每个协程执行ensure_future,封装为Task对象。
done, pending = asyncio.run(asyncio.wait(tasks))
# 如果现在想要获取当前协程中的返回值怎么办?
for item in done:
    print(item.result())
'''
运行结果:
开始任务
1
1
2
2
返回值
返回值
'''

如果tasks是一个全局列表:则不能在列表中创建task对象,只能传入协程函数对象 因为创建task对象需要有事件循环的存在

事件循环

作用:用来装载任务对象的(可以将事件循环当作是一个容器,容器中存放的是多个任务对象)

执行状态:若事件循环存放了多个任务对象且事件循环启动后,则事件循环对象就可以异步的将每一个任务对象对应的指定操作进行执行。

事件循环是异步编程中的重要组成部分,可以理解为一个死循环,可以检测并执行某些代码。

# 伪代码

任务列表 = [ 任务1, 任务2, 任务3,... ]

while True:
    可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
    
    for 就绪任务 in 可执行的任务列表:
        执行已就绪的任务
        
    for 已完成的任务 in 已完成的任务列表:
        在任务列表中移除 已完成的任务

 如果 任务列表 中的任务都已完成,则终止循环
'''
1.循环整个任务列表
2.将任务分为可以执行和执行完毕两种
3.将完成的任务从列表中剔除
4.当列表为空时中断循环
'''

使用方式:

import asyncio

# 创建方式一
# 创建事件循环对象
loop = asyncio.get_event_loop()

# 只将一个任务对象存储且启动事件循环对象
#loop.run_until_complete(task)

# 将任务列表中的多个任务对象注册到eventloop事件循环中且启动事件循环对象
loop.run_until_complete(asyncio.wait(tasks))  # tasks表示的是任务列表

# 创建方式二
asyncio.run(task)

注意事项:

在协程函数内部的实现语句中不可以出现不支持异步的模块代码,否则会终断整个异步效果。

在异步协程中如果出现了同步模块相关的代码(time、requests),那么就无法实现异步。

当在asyncio中遇到阻塞操作必须进行手动挂起(await)。

关于异步的更多用法,欢迎小伙伴后台留言哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值