在python中单线程,多线程,多进程对CPU的利用率实测以及GIL原理分析

简介: 首先关于在python中单线程,多线程,多进程对cpu的利用率实测如下: 单线程,多线程,多进程测试代码使用死循环。 1)单线程: 2)多线程: 3)多进程: 查看cpu使用效率: 开始观察分别执行时候cpu的使用效率:1)单线程执行的时候: 2)多线程执行的时候: 3)多进程执行的时候: 总结:1)单进程单线程时,对于双核CPU的利用率只能利用一个核,没有充分利用两个核。

首先关于在python中单线程,多线程,多进程对cpu的利用率实测如下:

 

单线程,多线程,多进程测试代码使用死循环。

 

1)单线程:

 

2)多线程:

 

3)多进程:

 

查看cpu使用效率:

 

开始观察分别执行时候cpu的使用效率:

1)单线程执行的时候:

 

2)多线程执行的时候:

 

3)多进程执行的时候:

 

总结:

1)单进程单线程时,对于双核CPU的利用率只能利用一个核,没有充分利用两个核。

2)单进程多线程时,对于双核CPU的来说,虽然两个核都用到的,不过很明显没有充分利用两个核,这里要说一个GIL(全局解释器锁)的概念:

GIL不同于线程之间的互斥锁,GIL并不是Python的特性,而是Cpython引入的一个概念。(Jpython,PYPY)

Python的代码由Python的解释器执行(CPython)。那么我们的代码什么时候被python解释器执行,由我们的GIL也就是全局解释器锁进行控制。

当我们有一个线程开始访问解释器的时候,GIL会将这把锁上锁,也就是说,其他线程无法再访问解释器,也就意味着,其他的线程无法再被执行。

 

GIL执行流程:

 

  1. 加锁GIL

  2. 切换到一个线程去执行。

  3. 运行。

  4. 解锁GIL

再次重复以上步骤。

对于下列代码GIL的执行流程:

 

import threading
import time
# 写两个函数,分别让两个线程去执行
# 这个两个函数,都要访问我的全局变量
number = 0

def test1(count):
    global number
    for i in range(count):
        number += 1
    print(number)

def test2(count):
    global number
    for i in range(count):
        number += 1
    print(number)


def main():
    th1 = threading.Thread(target=test1,args= (1000000,))
    th2 = threading.Thread(target=test2, args=(1000000,))
    th1.start()
    th2.start()

    time.sleep(5)
    print(number)

if __name__ == '__main__':
    main()

 

运行结果(这里充分的说明了多线程资源抢占问题):

流程图如下:

线程1在执行到对全局变量加一操作的时候全局解释器锁被收回,线程2申请并得到了全局解释器锁开始运行,在线程2执行完加一操作以后对全局变量进行了修改并释放了全局解释器锁。

这时线程1再次得到了全局解释器锁,从上次释放全局解释器锁的地方开始继续执行对全局变量加一的操作,记住,这里线程1中的全局变量还是开始的0,虽然线程2已经对其进行了加一的操作,但是线程1并不知道,线程1还是会接着上一次的位置开始执行,所以线程1在执行完加一操作的时候同样把1再次赋值给了全局变量num,也就是说,线程2执行完加一操作之后赋值过去的1又被线程1赋值过去的1所覆盖,加了两次等于加了一次!类似于协程,只是做了一个执行代码来回切换的操作!

所以在Python中,同一时刻,只能有一个线程被执行。所以Python中的多线程是假的

既然这样我们为什么还要用多线程呢?

其实多线程也有它的好处,例如我们在进行IO操作的时候,有效的组织了程序的阻塞,不至于一直无限的等待。

3)多进程时,对于双核CPU来说,每个进程的优先级都是同等的,所分配的资源也是相等的,两个进程的时候完全可以充分的利用双核CPU,而且由于计算密集型的任务完全是依靠于cpu的核数,所以需要尽量的完全利用cpu,这时候多进程的好处就能够完美的体现出来。

 

 

                                                                   -------  知识无价,汗水有情,如需搬运请注明出处,谢谢!

目录
相关文章
|
22天前
|
运维 监控 算法
时间序列异常检测:MSET-SPRT组合方法的原理和Python代码实现
MSET-SPRT是一种结合多元状态估计技术(MSET)与序贯概率比检验(SPRT)的混合框架,专为高维度、强关联数据流的异常检测设计。MSET通过历史数据建模估计系统预期状态,SPRT基于统计推断判定偏差显著性,二者协同实现精准高效的异常识别。本文以Python为例,展示其在模拟数据中的应用,证明其在工业监控、设备健康管理及网络安全等领域的可靠性与有效性。
552 13
时间序列异常检测:MSET-SPRT组合方法的原理和Python代码实现
|
1月前
|
机器学习/深度学习 算法 Python
机器学习特征筛选:向后淘汰法原理与Python实现
向后淘汰法(Backward Elimination)是机器学习中一种重要的特征选择技术,通过系统性地移除对模型贡献较小的特征,以提高模型性能和可解释性。该方法从完整特征集出发,逐步剔除不重要的特征,最终保留最具影响力的变量子集。其优势包括提升模型简洁性和性能,减少过拟合,降低计算复杂度。然而,该方法在高维特征空间中计算成本较高,且可能陷入局部最优解。适用于线性回归、逻辑回归等统计学习模型。
104 7
|
3月前
|
缓存 Rust 算法
从混沌到秩序:Python的依赖管理工具分析
Python 的依赖管理工具一直没有标准化,主要原因包括历史发展的随意性、社区的分散性、多样化的使用场景、向后兼容性的挑战、缺乏统一治理以及生态系统的快速变化。依赖管理工具用于处理项目中的依赖关系,确保不同环境下的依赖项一致性,避免软件故障和兼容性问题。常用的 Python 依赖管理工具如 pip、venv、pip-tools、Pipenv、Poetry 等各有优缺点,选择时需根据项目需求权衡。新工具如 uv 和 Pixi 在性能和功能上有所改进,值得考虑。
120 35
|
2天前
|
数据采集 数据可视化 数据挖掘
基于Python的App流量大数据分析与可视化方案
基于Python的App流量大数据分析与可视化方案
|
3月前
|
数据采集 数据可视化 数据挖掘
金融波动率的多模型建模研究:GARCH族与HAR模型的Python实现与对比分析
本文探讨了金融资产波动率建模中的三种主流方法:GARCH、GJR-GARCH和HAR模型,基于SPY的实际交易数据进行实证分析。GARCH模型捕捉波动率聚类特征,GJR-GARCH引入杠杆效应,HAR整合多时间尺度波动率信息。通过Python实现模型估计与性能比较,展示了各模型在风险管理、衍生品定价等领域的应用优势。
492 66
金融波动率的多模型建模研究:GARCH族与HAR模型的Python实现与对比分析
|
3月前
|
并行计算 安全 Java
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
在Python开发中,GIL(全局解释器锁)一直备受关注。本文基于CPython解释器,探讨GIL的技术本质及其对程序性能的影响。GIL确保同一时刻只有一个线程执行代码,以保护内存管理的安全性,但也限制了多线程并行计算的效率。文章分析了GIL的必要性、局限性,并介绍了多进程、异步编程等替代方案。尽管Python 3.13计划移除GIL,但该特性至少要到2028年才会默认禁用,因此理解GIL仍至关重要。
266 16
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
|
3月前
|
存储 缓存 Java
Python高性能编程:五种核心优化技术的原理与Python代码
Python在高性能应用场景中常因执行速度不及C、C++等编译型语言而受质疑,但通过合理利用标准库的优化特性,如`__slots__`机制、列表推导式、`@lru_cache`装饰器和生成器等,可以显著提升代码效率。本文详细介绍了这些实用的性能优化技术,帮助开发者在不牺牲代码质量的前提下提高程序性能。实验数据表明,这些优化方法能在内存使用和计算效率方面带来显著改进,适用于大规模数据处理、递归计算等场景。
102 5
Python高性能编程:五种核心优化技术的原理与Python代码
|
11月前
|
Python
【Python30天速成计划】10.异步以及多进程和多线程
【Python30天速成计划】10.异步以及多进程和多线程