python协程库_python协程 --- asyncio库

本文探讨了线程如何通过调度实现多任务,对比了传统线程的上下文切换问题,然后介绍了协程如何避免这些问题,通过asyncio库实现代替线程的顺序执行,实现异步IO操作,提升任务执行效率。重点讲解了协程的定义、任务调度、asyncio的使用方法以及协程对象与等待对象的应用。
摘要由CSDN通过智能技术生成

从线程理解协程

一个cpu想要同时实现多任务的执行,需要操作系统调度cpu去执行多个线程,每个线程执行不同的单个任务,从而实现多任务的执行。线程是操作系统的资源之一,创建或者销毁线程都由操作系统执行,每个线程都由自己独立的资源,例如临时变量的数据,函数调用的堆栈信息,或者当前线程执行的当前位置,发生线程切换时候,线程会保存这些资源,记录当前执行的状态,当cpu在次对该线程发起调度时,恢复上一次的离开时的状态执行。这些数据以及状态信息,通常称为线程上下文。

每个线程都有自己的线程上下文,而线程之间的切换必将引起上下文的切换,cpu执行一个线程时,这部分数据可以直接从寄存器或者多级缓存中读取,读取速度是可观的。线程的切换意味着另一个线程的被唤醒,新线程的数据需要被加载,就可能会覆盖掉部分的上个线程在寄存器或者缓存的数据,下次原线程恢复时需要重新从内存中读取数据到寄存器。多个cpu的情况下,这种方式情况更为严重,因为一个线程可以被随机的被某个cpu执行,上下文切换更为频繁,通常我们会考虑将一个线程绑定到某个cpu上,使其只被这个cpu调度,从而提高效率。

协程

线程的调度更加的耗费系统资源。而使用协程就可以避免这些问题。单个线程的执行是顺序执行的,如果需要完成3个任务(每个任务以一个函数表示),这三个任务将会一个个按照顺序执行,如果每个任务中包含部分IO操作,线程也只能同步阻塞等待IO返回,无法执行其他的任务。而协程实现了在这一个线程中同时调度执行这多个任务,避免这些耗时IO操作的同步阻塞等待,提高了任务的执行效率。

asyncio基本使用

定义任务

定义任务使用了async关键字,然后和普通函数的定义相同

importasyncio

async task1():print("task1 ++++")return "abc"

if __name__ = "__main__":

asyncio.run(task1())#run方法在3.7版本才实现

定义了任务task1,然后使用 asyncio.run(task1())执行了该任务。

在run函数执行任务时,实际上及创建了一个事件循环,这个事件循环会反复监听这个任务的状态,在多个任务时候,还会自动实现多个任务的调度。

事件循环

上面使用run函数运行时,会创建一个事件循环,事件循环中可以添加多个任务,并负责调度这些任务,在3.7之前,就需要手动的创建事件循环,添加任务才能保证事件的运行。

importasyncio

async task1():print("task1 ++++")return "abc"

if __name__ == "__main__":

loop= asyncio.get_event_loop() #获得一个事件循环

loop.run_until_complete(task1()) #运行事件循环,直到task1这个任务结束,事件循环终止

这里只添加了一个task1任务,如果还存在其他的如task2任务需要一并加入,需要使用gather方法

async task1():passasync task2():passasync task3():pass

if __name__ == "__main__":

loop= asyncio.get_event_loop() #获得一个事件循环

tasks =asyncio.gather(task1(), task2(), task3())

loop.run_until_complete(tasks)

运行任务

任务的运行需要交给事件循环,事件循环可以同时调度多个任务,前提是这些任务存在IO操作。因为协程任务始终是在一个线程上进行调度的,如果一个任务没有IO(例如无限的死循环),始终占据这个线程,其他的任务将无法被调度执行。

任务可以由事件循环统一调度,这些任务随机的被执行(聚集模式)也可以在一个任务中去启动一个子任务,然后等待子任务执行完毕在继续执行原任务(串行执行模式),等待任务的运行需要使用awati关键字,实现该任务同步阻塞等待子任务的效果。

importasyncio

async task1():print("task1 ++++")return "abc"async task2():print("task2 ++++")

await task1()return 123

if __name__ == "__main__":

loop= asyncio.get_event_loop() #获得一个事件循环

loop.run_until_complete(task2()) #运行事件循环,直到task1这个任务结束,事件循环终止

loop事件循环启动task2,task2中调用并同步等待task完成。也可以在task2中将task1添加到事件循环中,两个任务协同调用执行。

importasyncio

async task1():print("task1 ++++")return "abc"async task2():

t= asyncio.create_task(task1()) #在loop中添加一个任务,返回任务对象,该任务将会自动被调用执行

#await t # 也可以等待该t 任务执行完毕。

return 123

if __name__ == "__main__":

loop= asyncio.get_event_loop() #获得一个事件循环

loop.run_until_complete(task2()) #运行事件循环,直到task1这个任务结束,事件循环终

asyncio.sleep()

该方法也是一个协程对象,该协程对象的功能是等待一定的时常,通常我们在自己的任务中以await的方式调用 asyncio.sleep()方法,使得我们的协程任务等待一定时长再执行,等待时线程可以去执行其他的任务。

等待对象

三种等待对象:

协程对象:async def 方式定义的协程对象

task对象:使用asyncio.create_task方式返回的对象

future对象:

执行回调

task任务可以被注册一个回调函数,调用时会自动注入该task对象作为参数,所以回调函数只能是一参函数。如果回调函数需要有其他参数,需要提前绑定一部分参数值,使成为一参函数作为回调函数(可以使用装饰器,或者functool中的partail方法绑定实现参数绑定)。

importasyncioimportfunctoolsdefcallback(task, args):print(task, args)

task.resultreturn 123async task1():print("task1 ++++")return "abc"async task2():

t= asyncio.create_task(task1()) #在loop中添加一个任务,返回任务对象,该任务将会自动被调用执行

t.add_callback_done(

functools.partail(callback, args="abc") #callback函数绑定了一个args="abc"

)

asyncio.sleep(1)return 123

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值