当前位置: 首页 > news >正文

系统学习Python——并发模型和异步编程:进程、线程和GIL

分类目录:《系统学习Python》总目录


在文章《并发模型和异步编程:基础知识》我们简单介绍了Python中的进程、线程和协程。本文就着重介绍Python中的进程、线程和GIL的关系。

Python解释器的每个实例都是一个进程。使用multiprocessingconcurrent.futures库可以启动额外的Python进程。Python的subprocess库用于启动运行外部程序(不管使用何种语言编写)的进程。而Python解释器仅使用一个线程运行用户的程序和内存垃圾回收程序。使用threadingconcurrent.futures库可以启动额外的Python线程。对对象引用计数和解释器其他内部状态的访问受一个锁的控制,这个锁就是全局解释器锁(Global Interpreter Lock,GIL)。任意时间点上只有一个Python线程可以持有GIL。这意味着,任意时间点上只有一个线程能执行Python代码,与CPU核数量无关。为了防止一个Python线程无限期持有GIL,Python的字节码解释器默认每5毫秒暂停当前Python线程,释放GIL。被暂停的线程可以再次尝试获得GIL,但是如果有其他线程等待,那么操作系统调度程序可能会从中挑选一个线程开展工作。我们编写的Python代码无法控制GIL。但是,耗时的任务可由内置函数或C语言(以及其他能在Python/C API层级接合的语言)扩展释放GIL。Python标准库中发起系统调用的函数均可释放GIL。这包括所有执行磁盘I/O、网络I/O的函数,以及time.sleep()NumPy/SciPy库中很多CPU密集型函数,以及zlibbz2模块中执行压缩和解压操作的函数,也都释放GIL。在Python/C API层级集成的扩展也可以启动不受GIL影响的非Python线程。这些不受GIL影响的线程无法更改Python对象,但是可以读取或写入内存中支持缓冲协议的底层对象,例如bytearrayarray.arrayNumPy数组。

GIL对使用Python线程进行网络编程的影响相对较小,因为I/O函数释放GIL,而且与内存读写相比,网络读写的延迟始终很高。各个单独的线程无论如何都要花费大量时间等待,所以线程可以交错执行,对整体吞吐量不会产生重大影响。正如David Beazley所言:​“Python线程非常擅长什么都不做。​对GIL的争用会降低计算密集型Python线程的速度。对于这类任务,在单线程中依序执行的代码更简单,速度也更快。若想在多核上运行CPU密集型Python代码,必须使用多个Python进程。threading模块的文档对此做了很好的概括。

由于CPython有GIL,因此同一时间只有一个线程能执行Python代码(尽管有些旨在提升性能的库可以克服这个限制)​。如果我们希望应用程序充分地利用多核设备的计算资源,那么建议使用multiprocessingconcurrent.futures.ProcessPoolExecutor。然而,如果我们想同时运行多个I/O密集型任务,那么线程仍是最合适的模型。前一段开头指出那是“CPython实现细节”​,因为GIL不是Python语言规定的机制。Jython和IronPython就没有GIL。可惜,二者落后较多,还停留在Python2.7。高性能的PyPy解释器的2.7和3.7版本也有GIL。

本文没有提到协程,因为默认情况下,协程共用同一个Python线程,而且受异步框架提供的事件循环监管,所以不受GIL影响。在异步程序中也可以使用多个线程,但是最佳实践是在同一个线程中运行事件循环和所有协程,其他线程负责执行特定的任务。

参考文献:
[1] Mark Lutz. Python学习手册[M]. 机械工业出版社, 2018.
[2] 卢西亚诺·拉马略.流畅的Python 第2版(全2册) 编程语言[M].人民邮电出版社,2023.

http://www.lryc.cn/news/582208.html

相关文章:

  • 量子计算+AI芯片:光子计算如何重构神经网络硬件生态
  • 动手学深度学习13.7. 单发多框检测(SSD)-笔记练习(PyTorch)
  • Android 10 Gnss数据流程
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频质量评估与智能修复(337)
  • uniapp的navigator跳转功能
  • STEP 7 MicroWIN SMART V2.2 的详细安装步骤及注意事项
  • 【世纪龙科技】汽车钣金虚拟仿真教学实训软件
  • 源码推送到gitee码云仓库
  • 华为OD 二维伞的雨滴效应
  • JDBC 注册驱动的常用方法详解
  • Spring Data JPA基本方法调用规律
  • RK3588 Android SDK 实战全解析 —— 架构、原理与开发关键点
  • linux qt 使用log4cpp库
  • 对象存储-OSS
  • centos停止维护后更换yum源
  • Centos Docker 安装(100%成功)
  • 腾讯云 CDN 不支持 WebSocket 的现状与华为云 CDN 的替代方案-优雅草卓伊凡
  • 【DPDK应用篇】事件驱动架构:eventdev异步处理模型的设计与实现
  • 循环移位网络设计
  • ubuntu server系统 安装宝塔
  • 【build.gradle中的各种jdk或者是jvm,sdk版本作用区别,详细说明】
  • RKAndroid11-系统设置新增开关选项
  • Kotlin流操作符简介
  • 力扣网编程274题:H指数之计数排序(中等)
  • 分布式推客系统架构设计:从微服务到高性能计算的实践路径
  • 安装 Elasticsearch IK 分词器
  • Coco AI 实战(一):Coco Server Linux 平台部署
  • 前端技术博客汇总文档
  • 万物智联时代启航:鸿蒙OS重塑全场景开发新生态
  • 【读代码】深度解析TEN VAD:实时语音活动检测的高性能开源解决方案