python线程和协程,协程 python3

python线程和协程,协程 python3,Python进阶之协程详解

本文主要介绍Python的高级过程,具有一定的参考价值。感兴趣的朋友可以参考一下,希望能帮到你。

目录

协同学应用场景抢先式调度的缺点用户模式协同式调度的优点协同学的工作原理协同学在Python中的总结协同学

协程

协同例程(又称微线程)是一种多方协作的工作模式。在某个时刻,当前执行器主动让出控制流,并记住它的当前状态,以便当控制流返回时,它可以从最后一个被让出的位置继续执行。

简而言之,协同学的核心思想在于执行者的“主动投降”和控制流的“回收”。与线程的“抢占式调度”相比,协同调度是一种“协作式调度”。

协程的应用场景

抢占式调度的缺点

在I/O密集型场景下,抢占式调度的解决方案是“异步回调”机制。

问题是整个程序在某些场景下可读性很差。以图片下载为例。图片服务的中间站提供异步接口,发起方请求后立即返回。这时图片服务给发起方一个唯一的标识ID,图片服务下载完成后,将结果放入一个消息队列。此时,发起方需要继续消耗这个MQ,以获得下载是否完成的结果。

可以看到,整个逻辑被拆分成几个部分,每个子部分都会有状态迁移,这必然是以后bug的高发期。

用户态协同调度的优势

随着网络技术的发展和高并发性的需求,在网络操作、文件操作、数据库操作和消息队列操作等I/O繁重的操作场景中,协调过程提供的用户模式协同调度机制的优势逐渐被挖掘出来。

协同处理将I/O的处理能力从内核态操作系统返回给用户态程序本身。用户程序在执行I/O时,主动将yield CPU的执行权让给其他协程,多个协程是一种平等、对称、合作的关系。

协程的运行原理

当一个程序运行时,操作系统会为每个程序分配一个大小相同的虚拟内存空间,并将程序代码和所有静态数据加载到其中。然后,创建并初始化堆栈存储器,用于存储程序的局部变量、函数参数和返回地址;创建并初始化堆内存;创建和初始化与I/O相关的任务。当准备工作完成后,操作系统将CPU的控制权转移给新创建的进程,进程开始运行。

一个进程可以有一个或多个线程,同一个进程中的多个线程会共享进程中的所有系统资源,比如虚拟地址空间、文件描述符、信号处理等等。但是,同一进程中的多个线程有自己的调用堆栈和线程本地存储。

协同过程比线程轻。协同进程不是由操作系统内核管理,而是完全由用户程序控制。下图显示了协程与线程和进程之间的关系。可见,协进程本身是无法利用多核的,需要协同进程才能在多核平台上发挥作用。

进程之间的切换不需要涉及任何系统调用或任何阻塞调用。协程只在一个线程中执行,切换由用户状态控制,而线程的阻塞状态由操作系统内核完成,所以协程比线程节省了创建和切换线程的开销。在协进程中不存在同时写变量的冲突,所以不需要同步原语来守护关键块,比如互斥、信号量等。而且不需要操作系统的支持。

程通过“挂起点”主动让出CPU,并保存自身状态等待恢复。比如先在funcA函数中执行,运行一段时间后调用协程。协程开始执行,直到第一个起始点,然后像普通函数一样返回到funcA函数。funcA函数执行一些代码,然后再次调用协程。注意协程不同于普通函数。协处理不是从第一条指令开始执行,而是从最后一个挂起点开始执行。执行一段时间后,遇到第二个挂点。此时,co-process再次像正常函数一样返回到funcA函数,在funcA函数执行一段时间后,整个程序结束。

可以看出,协同进程的位置可以“自愿放弃”和“恢复”,因为解析器将其运行上下文保存在函数的运行时堆栈中。

Python 中的协程

Python协程支持经历了几个版本:

Python2.x对协程的支持有限,yield关键字支持的生成器实现了协程的部分功能但不完全。第三方库Gevent对协作的支持更好。Python3.4中提供了Asyncio模块,Python3.5中引入了async/await关键字,Python3.6中的Asyncio模块更加完善和稳定。Python3.7内置了async/await关键字。

async/await 的示例程序:

进口异步

从pathlib导入路径

导入日志记录

从urllib.request导入urlopen,request

导入操作系统

从时间导入时间

进口aiohttp

logging . basic config(level=logging。INFO,format=' %(asctime)s-%(name)s-%(level name)s-%(message)s ')

logger=logging . get logger(_ _ name _ _)

code flex _ IMAGES _ URL=[' https://code flex . co/WP-content/uploads/2021/01/pandas-data frame-python-1024 x512 . png ',

' https://code flex . co/WP-content/uploads/2021/02/github-actions-deployment-to-eks-with-kustomize-1024 x536 . jpg ',

' https://code flex . co/WP-content/uploads/2021/02/boto 3-S3-multipart-upload-1024 x536 . jpg ',

' https://code flex . co/WP-content/uploads/2018/02/Kafka-cluster-architecture . jpg ',

https://code flex . co/WP-content/uploads/2016/09/redis-cluster-topology . png ']

async def download_image_async(会话,目录,图像url):

download _ path=dir/OS . path . basename(img _ URL)

使用session.get(img_url)作为响应的异步:

用download_path.open('wb ')作为f:

虽然正确:

#在异步函数中使用await关键字意味着等待任务执行完成,也就是等待yeild放弃控制权。

#同时,asyncio使用事件循环event_loop来实现整个过程。

chunk=await response . content . read(512)

如果不是组块:

破裂

f.write(块)

logger.info('已下载:' img_url

#使用async关键字声明异步/协同例程函数。

#调用此函数时,它不会立即运行,而是返回一个协程对象,该对象随后将在event_loop中执行。

异步定义main():

images _ dir=Path(' code flex _ images ')

路径(' codeflex_images ')。mkdir(parents=False,exist_ok=True)

与aiohttp异步。ClientSession()作为会话:

tasks=[(download _ image _ async(session,images_dir,img _ URL))for img _ URL in code flex _ IMAGES _ URLS]

等待asyncio.gather(*tasks,return_exceptions=True)

if __name__=='__main__ ':

开始=时间()

# event_loop事件循环充当管理器,在几个协程函数之间切换控制。

event _ loop=asyncio . get _ event _ loop()

尝试:

event _ loop . run _ until _ complete(main()

最后:

event_loop.close()

logger.info('下载时间:%s秒',time() - start)

总结

本文到此为止。希望能帮到你,也希望你能多关注我们的更多内容!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: