python之异步编程

news/2024/7/8 7:17:25

一、异步编程概述

异步编程是一种并发编程的模式,其关注点是通过调度不同任务之间的执行和等待时间,通过减少处理器的闲置时间来达到减少整个程序的执行时间;异步编程跟同步编程模型最大的不同就是其任务的切换,当遇到一个需要等待长时间执行的任务的时候,我们可以切换到其他的任务执行;

与多线程和多进程编程模型相比,异步编程只是在同一个线程之内的的任务调度,无法充分利用多核CPU的优势,所以特别适合IO阻塞性任务;

python版本 3.9.5

二、python的异步框架模型

python提供了asyncio模块来支持异步编程,其中涉及到coroutines、event loops、futures三个重要概念;

event loops主要负责跟踪和调度所有异步任务,编排具体的某个时间点执行的任务;

coroutines是对具体执行任务的封装,是一个可以在执行中暂停并切换到event loops执行流程的特殊类型的函数;其一般还需要创建task才能被event loops调度;

futures负责承载coroutines的执行结果,其随着任务在event loops中的初始化而创建,并随着任务的执行来记录任务的执行状态;

异步编程框架的整个执行过程涉及三者的紧密协作;

首先event loops启动之后,会从任务队列获取第一个要执行的coroutine,并随之创建对应task和future;

然后随着task的执行,当遇到coroutine内部需要切换任务的地方,task的执行就会暂停并释放执行线程给event loop,event loop接着会获取下一个待执行的coroutine,并进行相关的初始化之后,执行这个task;

随着event loop执行完队列中的最后一个coroutine才会切换到第一个coroutine;

随着task的执行结束,event loops会将task清除出队列,对应的执行结果会同步到future中,这个过程会持续到所有的task执行结束;

三、顺序执行多个可重叠的任务

每个任务执行中间会暂停给定的时间,循序执行的时间就是每个任务执行的时间加和;

import time

def count_down(name, delay):
    indents = (ord(name) - ord('A')) * '\t'

    n = 3
    while n:
        time.sleep(delay)
        duration = time.perf_counter() - start
        print('-' * 40)
        print(f'{duration:.4f} \t{indents}{name} = {n}')
        n -= 1


start = time.perf_counter()

count_down('A', 1)
count_down('B', 0.8)
count_down('C', 0.5)
print('-' * 40)
print('Done')

# ----------------------------------------
# 1.0010 	A = 3
# ----------------------------------------
# 2.0019 	A = 2
# ----------------------------------------
# 3.0030 	A = 1
# ----------------------------------------
# 3.8040 		B = 3
# ----------------------------------------
# 4.6050 		B = 2
# ----------------------------------------
# 5.4059 		B = 1
# ----------------------------------------
# 5.9065 			C = 3
# ----------------------------------------
# 6.4072 			C = 2
# ----------------------------------------
# 6.9078 			C = 1
# ----------------------------------------
# Done

四、异步化同步代码

python在语法上提供了async、await两个关键字来简化将同步代码修改为异步;

async使用在函数的def关键字前边,标记这是一个coroutine函数;

await用在conroutine里边,用于标记需要暂停释放执行流程给event loops;

await 后边的表达式需要返回waitable的对象,例如conroutine、task、future等;

asyncio模块主要提供了操作event loop的方式;

我们可以通过async将count_down标记为coroutine,然后使用await和asyncio.sleep来实现异步的暂停,从而将控制权交给event loop;

async def count_down(name, delay, start):
    indents = (ord(name) - ord('A')) * '\t'

    n = 3
    while n:
        await asyncio.sleep(delay)
        duration = time.perf_counter() - start
        print('-' * 40)
        print(f'{duration:.4f} \t{indents}{name} = {n}')
        n -= 1

我们定义一个异步的main方法,主要完成task的创建和等待任务执行结束;

async def main():
    start = time.perf_counter()
    tasks = [asyncio.create_task(count_down(name,delay,start)) for name, delay in [('A', 1),('B', 0.8),('C', 0.5)]]
    await asyncio.wait(tasks)
    print('-' * 40)
    print('Done')

执行我们可以看到时间已经变为了执行时间最长的任务的时间了;

asyncio.run(main())

# ----------------------------------------
# 0.5010 			C = 3
# ----------------------------------------
# 0.8016 		B = 3
# ----------------------------------------
# 1.0011 	A = 3
# ----------------------------------------
# 1.0013 			C = 2
# ----------------------------------------
# 1.5021 			C = 1
# ----------------------------------------
# 1.6026 		B = 2
# ----------------------------------------
# 2.0025 	A = 2
# ----------------------------------------
# 2.4042 		B = 1
# ----------------------------------------
# 3.0038 	A = 1
# ----------------------------------------
# Done

五、使用多线程克服具体任务的异步限制

异步编程要求具体的任务必须是coroutine,也就是要求方法是异步的,否则只有任务执行完了,才能将控制权释放给event loop;

python中的concurent.futures提供了ThreadPoolExecutor和ProcessPoolExecutor,可以直接在异步编程中使用,从而可以在单独的线程或者进程至今任务;

import time
import asyncio
from concurrent.futures import ThreadPoolExecutor

def count_down(name, delay, start):
    indents = (ord(name) - ord('A')) * '\t'

    n = 3
    while n:
        time.sleep(delay)

        duration = time.perf_counter() - start
        print('-'*40)
        print(f'{duration:.4f} \t{indents}{name} = {n}')
        n -=1

async def main():
    start = time.perf_counter()
    loop = asyncio.get_running_loop()
    executor = ThreadPoolExecutor(max_workers=3)
    fs = [
       loop.run_in_executor(executor, count_down, *args)  for args in [('A', 1, start), ('B', 0.8, start), ('C', 0.5, start)]
    ]

    await asyncio.wait(fs)
    print('-'*40)
    print('Done.')

asyncio.run(main())

# ----------------------------------------
# 0.5087 			C = 3
# ----------------------------------------
# 0.8196 		B = 3
# ----------------------------------------
# 1.0073 	A = 3
# ----------------------------------------
# 1.0234 			C = 2
# ----------------------------------------
# 1.5350 			C = 1
# ----------------------------------------
# 1.6303 		B = 2
# ----------------------------------------
# 2.0193 	A = 2
# ----------------------------------------
# 2.4406 		B = 1
# ----------------------------------------
# 3.0210 	A = 1
# ----------------------------------------
# Done.

http://lihuaxi.xjx100.cn/news/957606.html

相关文章

盘点 Vue3 与 Vue2 的区别

一、Vue3 与 Vue2 区别详述 1. 生命周期 对于生命周期来说,整体上变化不大,只是大部分生命周期钩子名称上 “on”,功能上是类似的。不过有一点需要注意,Vue3 在组合式API(Composition API,下面展开&…

自动化测试实战篇(8),jmeter并发测试登录接口,模拟从100到1000个用户同时登录测试服务器压力

首先进行使用jmeter进行并发测试之前就需要搞清楚线程和进程的区别还需要理解什么是并发、高并发、并行。还需要理解高并发中的以及老生常谈的,TCP三次握手协议和TCP四次握手协议**TCP三次握手协议指:****TCP四次挥手协议:**进入Jmeter&#…

Qt 防止程序退出

文章目录摘要QWidgetQML方法 1方法 2关键字: Qt、 eventFilter、 Close、 键盘、 任务管理器摘要 今天要聊得内容还是怎么防止别人关闭我的程序,之前都是在win下面,一般都是用过钩子连捕获键盘事件,完了吧对应的事件忽略&#x…

Flutter Button 实例

大家好,我是 17。 在上篇文章 使用 Flutter Button 介绍了如何修改 button 的样式,本文来具体实践一下。 本文列举一些常用的 button 效果,以便在用到的时候方便使用。因为 ElevatedButton 最常用,所以大多以 ElevatedButton 举…

干货 | 提升前端工程化,携程 Design2Code 从零到一的实践

作者简介by,携程高级研发经理,专注低代码平台搭建和前端智能化技术。Jialu,携程研发总监,专注大前端技术和工程化的研究与发展。一、 背景在软件开发过程中,团队协作效率的提高是我们共同关注的问题。为了解决这一问题…

Java多线程(二)——同步

这一节主要是继上次提到的线程同步三大方法:同步代码块、同步方法、Lock锁。同步代码块,把出现线程安全问题的核心代码给上锁。还是继上次的例子,对代码块加上synchronized ("getMoney") {}之后就不会出现线程安全问题了&#xff1…

元数据的类型

元数据通常分为三种类型:业务元数据、技术元数据和操作元数据。这些类别使人们能够理解属于元数据总体框架下的信息范围,以及元数据的产生过程。也就是说,这些类别也可能导致混淆,特别是当人们对一组元数据属于哪个类别或应该由谁…

Linux 进程:exit和_exit的辨析

目录1.接口与函数2.缓冲区3.exit 与 _exit(1)_exit(2)exit这里来认识exit函数和 _exit接口 ,它们的作用是类似的,都是在调用后退出程序,可以在程序的任何地方调用。 1.接口与函数 exit函数和_exit接口,一个函数,一个…