|
文档结构1、概念简介2、使用场景3、实现方式4、代码案例5、注意事项参考手册:https://docs.python.org/zh-cn/3.10/library/asyncio-task.html1、概念简介协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行),是一种用户状态内的上下文切换技术,其实就是通过一个线程实现代码块相互切换执行,因此也称为轻量级的线程。协程的切换完全由程序控制,而非通过操作系统内核来实现,因此对资源的开销更小;2、使用场景通过async&awiat实现异步编程,是Python实现异步操作的主流技术;如果一个对象可以在await语句中使用,那么它就是可等待对象。许多asyncioAPI都被设计为接受可等待对象。可等待对象有三种主要类型:Coroutine(协程),task(任务)和Future;3、实现方式1)greenlet:一个第三方模块,需要提前安装pip3installgreenlet才能使用;2)yield生成器:借助生成器的特点亦可以实现协程代码;3)asyncio:在python3.4种引入的模块,用于编写协程代码;说明:主要通过装饰器@asyncio.coroutine来实现协程函数定义;Python3.8之后@asyncio.coroutine装饰器会被移除,推荐使用async&awit关键字实现协程代码。4)async&awiat:在python3.5中引入的两个关键字,结合asyncio模块使用;4、代码案例场景:目前解析一个xml文件,需要解析的节点处理为一个列表,然后希望并发解析列表里的节点数据;实现方式1:多线程方式1)创建函数parseNode(node,delay);2)循环该节点列表,每个节点分配一个线程去执行parseNode(node,delay),同时将该线程加入线程列表thread_list;3)循环线程列表,执行th.join(),使主线程等待每个线程执行完成;4)执行主线程后续步骤;实现方式2:协程方式代码1:直接执行协程对象#-*-coding=utf-8-*-"""@DevToolyCharm@Author:xxx@DateTime:2024/1/2915:36@FileName:coroutineDemo.py"""importasyncioimporttimeasyncdefparseNode(th_n:int,delay:int):print("协程{}-开始执行!".format(th_n))awaitasyncio.sleep(delay)print("协程{}-执行结束,耗时{}秒!".format(th_n,delay))node_list=[xforxinrange(1,21)]print("主线程-运行开始")v_start=time.time()forxinnode_list:asyncio.run(parseNode(x,x))v_end=time.time()print("主线程-运行结束,耗时{}秒!".format(round((v_end-v_start),2)))1234567891011121314151617181920212223242526输出结果1:E:\PythonProject\dataSpider\venv\Scripts\python.exeE:/PythonProject/dataSpider/coroutineDemo.py主线程-运行开始协程1-开始执行!协程1-执行结束,耗时1秒!协程2-开始执行!协程2-执行结束,耗时2秒!协程3-开始执行!协程3-执行结束,耗时3秒!协程4-开始执行!协程4-执行结束,耗时4秒!协程5-开始执行!协程5-执行结束,耗时5秒!协程6-开始执行!协程6-执行结束,耗时6秒!协程7-开始执行!协程7-执行结束,耗时7秒!协程8-开始执行!协程8-执行结束,耗时8秒!协程9-开始执行!协程9-执行结束,耗时9秒!协程10-开始执行!协程10-执行结束,耗时10秒!协程11-开始执行!协程11-执行结束,耗时11秒!协程12-开始执行!协程12-执行结束,耗时12秒!协程13-开始执行!协程13-执行结束,耗时13秒!协程14-开始执行!协程14-执行结束,耗时14秒!协程15-开始执行!协程15-执行结束,耗时15秒!协程16-开始执行!协程16-执行结束,耗时16秒!协程17-开始执行!协程17-执行结束,耗时17秒!协程18-开始执行!协程18-执行结束,耗时18秒!协程19-开始执行!协程19-执行结束,耗时19秒!协程20-开始执行!协程20-执行结束,耗时20秒!主线程-运行结束,耗时210.15秒!Processfinishedwithexitcode0123456789101112131415161718192021222324252627282930313233343536373839404142434445从输出结果看,明显是串行执行的,下面用另外一种代码实现并行执行;代码2:执行代理主函数对象Python在3.7中加入了asyncio.create_task()函数,低版本的Python可以改用低层级的asyncio.ensure_future()函数;asyncio.create_task()函数用来并发运行作为asyncio任务的多个协程。#-*-coding=utf-8-*-"""@DevToolyCharm@Author:xxx@DateTime:2024/1/2915:36@FileName:coroutineDemo.py"""importasyncioimporttimeasyncdefparseNode(th_n:int,delay:int):print("协程{}-开始执行!".format(th_n))awaitasyncio.sleep(delay)print("协程{}-执行结束,耗时{}秒!".format(th_n,delay))asyncdefproxyMain():exec_list=[xforxinrange(1,21)]task_list=list()#任务列表forxinexec_list:task=asyncio.create_task(parseNode(x,x))task_list.append(task)#将每个任务都放入任务列表foryintask_list:awaity#使主线程等待每个任务执行完成v_start=time.time()print("主线程-运行开始")asyncio.run(proxyMain())v_end=time.time()print("主线程-运行结束,耗时{}秒!".format(round((v_end-v_start),2)))12345678910111213141516171819202122232425262728293031323334输出结果2:E:\PythonProject\dataSpider\venv\Scripts\python.exeE:/PythonProject/dataSpider/encryptDemo.py主线程-运行开始协程1-开始执行!协程2-开始执行!协程3-开始执行!协程4-开始执行!协程5-开始执行!协程6-开始执行!协程7-开始执行!协程8-开始执行!协程9-开始执行!协程10-开始执行!协程11-开始执行!协程12-开始执行!协程13-开始执行!协程14-开始执行!协程15-开始执行!协程16-开始执行!协程17-开始执行!协程18-开始执行!协程19-开始执行!协程20-开始执行!协程1-执行结束,耗时1秒!协程2-执行结束,耗时2秒!协程3-执行结束,耗时3秒!协程4-执行结束,耗时4秒!协程5-执行结束,耗时5秒!协程6-执行结束,耗时6秒!协程7-执行结束,耗时7秒!协程8-执行结束,耗时8秒!协程9-执行结束,耗时9秒!协程10-执行结束,耗时10秒!协程11-执行结束,耗时11秒!协程12-执行结束,耗时12秒!协程13-执行结束,耗时13秒!协程14-执行结束,耗时14秒!协程15-执行结束,耗时15秒!协程16-执行结束,耗时16秒!协程17-执行结束,耗时17秒!协程18-执行结束,耗时18秒!协程19-执行结束,耗时19秒!协程20-执行结束,耗时20秒!主线程-运行结束,耗时20.01秒!Processfinishedwithexitcode01234567891011121314151617181920212223242526272829303132333435363738394041424344454647从输出结果看,执行时间从之前的210秒降到了20秒;说明:上述代码中必须先全部使用create_task任务,然后在调用每个任务;代码3:asyncio.gather(task)任务组#-*-coding=utf-8-*-"""@DevToolyCharm@Author:xxx@DateTime:2024/1/2915:36@FileName:coroutineDemo.py"""importasyncioimporttimeasyncdefparseNode(th_n:int,delay:int):print("协程{}-开始执行!".format(th_n))awaitasyncio.sleep(delay)print("协程{}-执行结束,耗时{}秒!".format(th_n,delay))asyncdefproxyMain():exec_list=[xforxinrange(1,21)]task_list=list()#任务列表task_set=asyncio.gather()forxinexec_list:task=asyncio.create_task(parseNode(x,x))task_set=asyncio.gather(task)#将每个任务都放入任务列表awaittask_setv_start=time.time()print("主线程-运行开始")asyncio.run(proxyMain())v_end=time.time()print("主线程-运行结束,耗时{}秒!".format(round((v_end-v_start),2)))12345678910111213141516171819202122232425262728293031323334代码4:python在3.11版本加入了asyncio.TaskGroup类,提供了create_task()更现代化的替代;#-*-coding=utf-8-*-"""@DevToolyCharm@Author:gzh@DateTime:2024/2/115:57@FileName:coroutDemo.py"""importasyncioimporttimeasyncdefparseNode(th_n:int,delay:int):print("协程{}-开始执行!".format(th_n))awaitasyncio.sleep(delay)print("协程{}-执行结束,耗时{}秒!".format(th_n,delay))asyncdefproxyMain():exec_list=[xforxinrange(1,21)]asyncwithasyncio.TaskGroup()astg:forxinexec_list:task=tg.create_task(parseNode(x,x))v_start=time.time()print("主线程-运行开始")asyncio.run(proxyMain())v_end=time.time()print("主线程-运行结束,耗时{}秒!".format(round((v_end-v_start),2)))123456789101112131415161718192021222324252627282930315、注意事项1)直接调用一个使用async定义的函数,是不会执行的;2)协程执行的业务代码一般不会写在主函数中,而是定义个代理主函数;即通过一个协程调用其他协程;============================================over==========================================
|
|