Python 中多线程与多处理之间的区别

news2024/11/10 18:40:00

一、说明

   在本文中,我们将学习 Python 中多线程和多处理的内容、原因和方式。在我们深入研究代码之前,让我们了解这些术语的含义。

二、基本术语和概念

   程序是一个可执行文件,它由一组执行某些任务的指令组成,通常存储在计算机的磁盘上。
   进程就是我们所说的程序,它已与运行所需的所有资源一起加载到内存中。它有自己的内存空间。
   线程是进程中的执行单元。一个进程可以有多个线程作为其一部分运行,其中每个线程使用进程的内存空间并与其他线程共享。
   多线程是一种技术,其中进程生成多个线程以执行不同的任务,大约在同一时间,一个接一个。这给你一种错觉,即线程是并行运行的,但实际上它们是以并发方式运行的。在 Python 中,全局解释器锁 (GIL) 阻止线程同时运行。
多处理是一种实现最真实形式的并行性的技术。多个进程跨多个 CPU 内核运行,这些内核之间不共享资源。每个进程可以在自己的内存空间中运行许多线程。在 Python 中,每个进程都有自己的 Python 解释器实例,负责执行指令。
   现在,让我们进入程序,我们尝试以六种不同的方式执行两种不同类型的函数:IO 绑定和 CPU 绑定。在 IO 绑定函数中,我们要求 CPU 闲置并打发时间,而在 CPU 绑定函数中,CPU 将忙于产生一些数字。

要求:

  • 一台 Windows 计算机(我的机器有 6 个内核)。
  • 已安装 Python 3.x。
  • 任何用于编写 Python 程序的文本编辑器/IDE(我在这里使用 Sublime Text)。

   注意:以下是我们程序的结构,它将在所有六个部分中通用。在提到它的地方 # YOUR CODE SNIPPET HERE,将其替换为每个部分的代码片段。

import time, os
from threading import Thread, current_thread
from multiprocessing import Process, current_process


COUNT = 200000000
SLEEP = 10

def io_bound(sec):

	pid = os.getpid()
	threadName = current_thread().name
	processName = current_process().name

	print(f"{pid} * {processName} * {threadName} \
		---> Start sleeping...")
	time.sleep(sec)
	print(f"{pid} * {processName} * {threadName} \
		---> Finished sleeping...")

def cpu_bound(n):

	pid = os.getpid()
	threadName = current_thread().name
	processName = current_process().name

	print(f"{pid} * {processName} * {threadName} \
		---> Start counting...")

	while n>0:
		n -= 1

	print(f"{pid} * {processName} * {threadName} \
		---> Finished counting...")

if __name__=="__main__":
	start = time.time()

	# YOUR CODE SNIPPET HERE

	end = time.time()
	print('Time taken in seconds -', end - start)

三、进程对CPU绑定

第 1 部分:一个接一个地运行 IO 绑定任务两次…

# Code snippet for Part 1
io_bound(SLEEP)
io_bound(SLEEP)

   在这里,我们要求 CPU 执行函数 io_bound(),该函数将整数(此处为 10)作为参数,并要求 CPU 休眠几秒钟。此执行总共需要 20 秒,因为每个函数执行需要 10 秒才能完成。请注意,它是同一个 MainProcess 使用其默认线程 MainThread 一个接一个地调用我们的函数两次。
在这里插入图片描述

第 2 部分:使用线程运行受 IO 绑定的任务…

# Code snippet for Part 2
t1 = Thread(target = io_bound, args =(SLEEP, ))
t2 = Thread(target = io_bound, args =(SLEEP, ))
t1.start()
t2.start()
t1.join()
t2.join()

   在这里,让我们使用 Python 中的线程来加快函数的执行速度。线程 Thread-1 和 Thread-2 由我们的 MainProcess 启动,每个线程几乎同时调用我们的函数。两个线程同时完成休眠 10 秒的工作。这大大缩短了整个程序的总执行时间,减少了 50%。因此,多线程是执行任务的首选解决方案,其中 CPU 的空闲时间可用于执行其他任务。因此,通过利用等待时间来节省时间。
在这里插入图片描述

第 3 部分:一个接一个地运行两次 CPU 密集型任务…

# Code snippet for Part 3
cpu_bound(COUNT)
cpu_bound(COUNT)

   在这里,我们将调用我们的函数 cpu_bound(),它将一个大数字(此处为 200000000)作为参数,并在每一步将其递减,直到它为零。我们的 CPU 被要求在每次函数调用时进行倒计时,这大约需要 12 秒(这个数字可能因您的计算机而异)。因此,整个程序的执行花了我大约 26 秒才能完成。请注意,MainProcess 再次在其默认线程 MainThread 中一个接一个地调用该函数两次。

在这里插入图片描述

第 4 部分:线程可以加快我们受 CPU 限制的任务吗?

# Code snippet for Part 4
t1 = Thread(target = cpu_bound, args =(COUNT, ))
t2 = Thread(target = cpu_bound, args =(COUNT, ))
t1.start()
t2.start()
t1.join()
t2.join()

   好的,我们刚刚证明了线程对于多个 IO 绑定任务的效果非常好。让我们使用相同的方法来执行 CPU 密集型任务。好吧,它最初确实同时启动了我们的线程,但最终,我们看到整个程序执行花费了大约 40 秒!刚刚发生了什么?这是因为当 Thread-1 启动时,它获得了全局解释器锁 (GIL),从而阻止 Thread-2 使用 CPU。因此,Thread-2 必须等待 Thread-1 完成其任务并释放锁,以便它可以获取锁并执行其任务。这种锁的获取和释放增加了总执行时间的开销。因此,我们可以肯定地说,对于需要 CPU 处理某事的任务来说,线程并不是一个理想的解决方案。
在这里插入图片描述

第 5 部分:那么,将任务拆分为单独的流程是否有效?

# Code snippet for Part 5
p1 = Process(target = cpu_bound, args =(COUNT, ))
p2 = Process(target = cpu_bound, args =(COUNT, ))
p1.start()
p2.start()
p1.join()
p2.join()

   让我们切入正题。多处理就是答案。在这里,MainProcess 启动了两个子进程,它们具有不同的 PID,每个子进程都负责将数字减少到零。每个进程并行运行,使用单独的 CPU 内核和它自己的 Python 解释器实例,因此整个程序执行只需 12 秒。请注意,输出可能以无序方式打印,因为进程彼此独立。每个进程都在其自己的默认线程 MainThread 中执行函数。在程序执行期间打开任务管理器。您可以看到 Python 解释器的 3 个实例,MainProcess、Process-1 和 Process-2 各一个。您还可以看到,在程序执行期间,两个子进程的功耗为“非常高”,因为它们正在执行的任务实际上正在对它们自己的 CPU 内核造成影响,如 CPU 性能图中的峰值所示。

在这里插入图片描述

第 6 部分:我们对 IO 绑定任务使用多处理…

# Code snippet for Part 6
p1 = Process(target = io_bound, args =(SLEEP, ))
p2 = Process(target = io_bound, args =(SLEEP, ))
p1.start()
p2.start()
p1.join()
p2.join()

   现在我们已经对多处理帮助我们实现并行性有了大致的了解,我们将尝试使用这种技术来运行我们的 IO 绑定任务。我们确实观察到结果是非凡的,就像在多线程的情况下一样。由于进程 1 和进程 2 正在执行要求自己的 CPU 内核闲置几秒钟的任务,因此我们没有发现高功耗。但是,进程的创建本身就是一项 CPU 繁重的任务,并且比创建线程需要更多的时间。此外,进程需要的资源比线程多。因此,最好将多处理作为 IO 绑定任务的第二个选项,多线程是第一个选项。
在这里插入图片描述
   嗯,那是一段相当长的旅程。我们看到了执行一项任务的六种不同方法,大约需要 10 秒,具体取决于任务对 CPU 的影响是轻还是重。

四、结论

   底线:IO 绑定任务的多线程处理。CPU密集型任务的多处理。

Python 中的多线程Python 中的多处理
它实现了并发性。它实现了并行性。
在并行计算的情况下,Python 不支持多线程。Python 在并行计算的情况下支持多处理。
在多线程中,单个进程同时生成多个线程。在多处理中,多个线程同时跨多个内核运行。
无法对多线程进行分类。多处理可以分为对称或非对称。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1398857.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据恢复轻松搞定:如何处理.adver的数据勒索

引言: 勒索病毒日益猖獗,.adver 勒索病毒作为新兴威胁之一,给用户的数据安全带来了不小的挑战。本文将深入介绍.adver勒索病毒的特点、被加密数据的恢复方法,以及全面的预防措施,帮助您更好地了解并应对这一数字威胁。…

【每日一题】按分隔符拆分字符串

文章目录 Tag题目来源解题思路方法一:遍历方法二:getline 写在最后 Tag 【遍历】【getline】【字符串】【2024-01-20】 题目来源 2788. 按分隔符拆分字符串 解题思路 方法一:遍历 思路 分隔符在字符串开始和结束位置时不需要处理。 分隔…

16k+ start 一个开源的的监控系统部署教程

安装条件 Linux或macOS系统 4GB内存 开放 33014、33174、3183端口 1.安装 1、下载源码 首先使用 git 克隆源码到本地 git clone -b main https://github.com/SigNoz/signoz.git && cd signoz/deploy/ 方式1:运行 install.sh 脚本一键安装 ./install.s…

HAL库配置RS485通信

在配置好串口的基础上完成RS485的配置 一、使能RS485的发送和接收模式引脚 __HAL_RCC_GPIOG_CLK_ENABLE();//高电平是发送模式,低电平是接收模式,默认是接收模式HAL_GPIO_WritePin(PG4_RS485_DIR1_Port, PG4_RS485_DIR1_Pin, GPIO_PIN_RESET);GPIO_Init…

代码随想录算法训练营Day24 | 122.买卖股票的最佳时期、55.跳跃游戏、45.跳跃游戏||

LeetCode 122 买卖股票的最佳时期 本题思路:记录每天的利润值,第一天的为 0 ,第二天的当天的减去前一天的。然后遍历相加为正的利润值。最后得到的结果就是最大利润。 class Solution {public int maxProfit(int[] prices) {int res 0;for(…

DiffMIC:融合局部和全局分析,基于扩散模型的医学图像分类方法

DiffMIC:基于扩散模型的医学图像分类方法 DiffMIC的核心思想糖尿病视网膜病变分级 网络结构去噪扩散模型:提升特征清晰度双粒度条件引导(DCG):融合局部和全局分析条件特定的最大均值差异(MMD)正…

Linux:软件包管理器 yum

提示:以下指令均在Xshell 7 中进行 什么是软件包? 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序。 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安 装程序)放…

Linux上软件安装

软件安装常见方式 二进制发布包 软件已经针对具体平台编译打包发布,只要解压,修改配置即可。 RPM包 软件已经按照redhat的包管理工具规范RPM进行打包发布,需要获取到相应的软件RPM发布包,然后用RPM命令进行安装,但…

Internet Download Manager 6.42.3 (IDM) 中文破解免激活绿色版

Internet Download Manager 6.42.3中文破解版,全球最佳下载利器。Internet Download Manager (简称IDM) 是一款Windows 平台功能强大的多线程下载工具,国外非常受欢迎。支持断点续传,支持嗅探视频音频,接管所有浏览器,…

可视化k8s页面(Kubepi)

Kubepi是一个简单高效的k8s集群图形化管理工具,方便日常管理K8S集群,高效快速的查询日志定位问题的工具 随便在哪个节点部署,我这里在主节点部署 docker pull kubeoperator/kubepi-server docker run --privileged -itd --restartunless-st…

动态规划汇总

作者推荐 视频算法专题 简介 动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的&#x…

端杂七杂八系列篇四-Java8篇

后端杂七杂八系列篇四-Java8篇 ① Lombok插件① RequiredArgsConstructor② SneakyThrows③ UtilityClass④ Cleanup ② Lambda 4个常用的内置函数① Function<T, R> - 接受一个输入参数并返回一个结果② Consumer - 接受一个输入参数&#xff0c;并执行某种操作&#xf…

Jumpserver中安装httpx

查看已安装的包 pip freeze # python -V # Python 2.7.5安装anyio 刚开始直接pip install httpx&#xff0c;未设置版本号&#xff0c;因已有idna2.7&#xff0c;所以出现版本不兼容的现象 anyio 3.0.0 requires idna>2.8, but you have idna 2.7 which is incompatible.…

Red Hat Enterprise Linux 8.9 安装图解

引导和开始安装 选择倒计时结束前&#xff0c;通过键盘上下键选择下图框选项&#xff0c;启动图形化安装过程。需要注意的不同主板默认或者自行配置的固件类型不一致&#xff0c;引导界面有所不同。也就是说使用UEFI和BIOS的安装引导界面是不同的&#xff0c;如图所示。若手动调…

【VRTK】【Unity】【游戏开发】项目实践-小游戏

课程配套学习项目源码资源下载 https://download.csdn.net/download/weixin_41697242/88485426?spm=1001.2014.3001.5503 【背景】 本篇做一个综合实践,应用所学做一个mini VR游戏。不用写一行代码,纯粹理解并使用现有的VRTK脚本。 【导入所需资源】 在之前VRTK项目基础上…

Pix2Pix理论与实战

本文为&#x1f517;365天深度学习训练营 中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 我的环境&#xff1a; 1.语言&#xff1a;python3.7 2.编译器&#xff1a;pycharm 3.深度学习框架Pytorch 1.8.0cu111 一、引入 在之前的学习中&#xff0c;我们知道…

【51单片机Keil+Proteus8.9】温室盆栽灌溉系统

实验五 实验名称 温室盆栽灌溉系统 软件设计&#xff1a; 1. 定义对应的引脚和端口的别名。 2. 编写延时函数&#xff0c;用于控制程序的执行速度。 3. 编写LCD控制函数&#xff0c;包括发送命令和发送数据两种操作。 4. 编写显示函数&#xff0c;用于在LCD上显示字符串…

docker硬件交互 _ROS2

docker硬件交互 _ROS2 将自己需要挂载的设备接到主板上&#xff0c;在宿主机中建立udev规则&#xff08;/etc/udev/rules.d/&#xff09;然后在开启容器时&#xff0c;将设置了规则的devices 通过 --device/dev/myserial --device/dev/rplidar 等 参数挂载到docker容器中 doc…

WordPress怎么禁用文章和页面古腾堡块编辑器?如何恢复经典小工具?

现在下载WordPress最新版来搭建网站&#xff0c;默认的文章和页面编辑器&#xff0c;以及小工具都是使用古腾堡编辑器&#xff08;Gutenberg块编辑器&#xff09;。虽然有很多站长说这个编辑器很好用&#xff0c;但是仍然有很多站长用不习惯&#xff0c;觉得操作太难了&#xf…

【Linux取经路】初探进程地址空间

文章目录 一、历史问题回顾二、语言层面的地址空间2.1 验证 三、虚拟地址的引入3.1 初步解释这种现象——引入地址空间的概念3.2 再来粗粒度理解上面的现象 四、细节解释4.1 地址空间究竟是什么&#xff1f;4.2为什么要有地址空间4.3 页表4.3.1 CR3寄存器4.3.2 页表是由页表项组…