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

一、说明

   在本文中,我们将学习 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_processCOUNT = 200000000
SLEEP = 10def io_bound(sec):pid = os.getpid()threadName = current_thread().nameprocessName = current_process().nameprint(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().nameprocessName = current_process().nameprint(f"{pid} * {processName} * {threadName} \---> Start counting...")while n>0:n -= 1print(f"{pid} * {processName} * {threadName} \---> Finished counting...")if __name__=="__main__":start = time.time()# YOUR CODE SNIPPET HEREend = 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.mzph.cn/news/636223.shtml

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

相关文章

IntelliJ IDEA 常用快捷键一览表(通用型,提高编写速度,类结构、查找和查看源码,替换与关闭,调整格式)

文章目录 IntelliJ IDEA 常用快捷键一览表1-IDEA的日常快捷键第1组:通用型第2组:提高编写速度(上)第3组:提高编写速度(下)第4组:类结构、查找和查看源码第5组:查找、替换…

第二课:BERT

文章目录 第二课:BERT1、学习总结:为什么要学习BERT?预训练模型的发展历程BERT结构BERT 输入BERT EmbeddingBERT 模型构建BERT self-attention 层BERT self-attention 输出层BERT feed-forward 层BERT 最后的Add&NormBERT EncoderBERT 输…

修复uni-simple-router@2.0.7版本query参数null的bug

问题:query参数为null或者为空时,插件内部参数校验问题导致的会报错:TypeError: Cannot convert undefined or null to object at Function.keys 源码修改如下: 通过打补丁的方式修复query参数类型校验问题 1. 安装patch-packag…

Python实现两个列表相加的方法汇总

1. 使用 “” 运算符 通过 “” 运算符将两个列表相加,得到一个新的列表。例如: list1 [1, 2, 3] list2 [4, 5, 6] result list1 list2 print(result) # [1, 2, 3, 4, 5, 6]2. 使用 extend 方法 使用 extend 方法将一个列表中的元素逐个添加到另…

【24.1.19】

24.1.19 本周工作内容下周工作计划 本周工作内容 本周的话主要的一个工作还是第三部分页面部分的完成工作,那就先来汇报一下第三部分的工作进度,第三部分的页面工作呢已经完成啦,就在刚刚提交啦全部的代码,那么这一部分的工作呢也…

使 a === 1 a === 2 a === 3 为 true 的几种“下毒“方法

前言 这算得上是近些年的前端网红题了,曾经对这种网红题非常抵触,认为非常没有意义。 看到了不少人有做分享,有各种各样的方案,有涉及到 JS 非常基础的知识点,也不得不感叹解题者的脑洞之大。 但是,拿来…

传奇服务器搭建

传奇服务器搭建 传奇是一款非常经典的游戏,自从它推出以来就深受玩家们的喜爱。如果你也想要在自己的服务器上搭建一个传奇,那么本文将为你提供一些有用的信息。 首先,我们需要知道什么是传奇服务器。简单来说,它就是一个能够让…

公司OA办公系统使用阿里云服务器怎么选配置?

公司OA、ERP等办公系统如何选择阿里云服务器配置?可以选择第七代企业级独享型云服务器,ECS通用型g7、计算型c7或内存型r7实例,4核CPU8G内存、8核16G、4核16G等配置,活动 https://t.aliyun.com/U/bLynLC 实例规格使用场景vCPU内存…

线程池c++实现

线程池c实现 概述 线程池(Thread Pool)是一种并发编程的设计模式,它用于管理和重复使用线程,以提高程序的性能和资源利用率。线程池通过维护一组预先创建的线程,这些线程可以在需要时被重复使用,而不是为…

HarmonyOS鸿蒙学习基础篇 - 什么是HarmonyOS

概述 HarmonyOS是华为开发的一款面向未来的全场景分布式智慧操作系统,将逐步覆盖18N全场景终端设备; 对消费者而言 HarmonyOS用一个‘统一的软件系统’ 从根本上解决消费者面对大量智能终端体验割裂的问题,为消费者带来同意便利安全的智慧化全…

相关系数(皮尔逊相关系数和斯皮尔曼相关系数)

本文借鉴了数学建模清风老师的课件与思路,可以点击查看链接查看清风老师视频讲解:5.1 对数据进行描述性统计以及皮尔逊相关系数的计算方法_哔哩哔哩_bilibili 注:直接先看 ( 三、两个相关系数系数的比较 ) 部分&#x…

C++17新特性(一)基本语言特性

1. 结构化绑定 假设你有两个不同成员的结构体: struct MyStruct {int i 0;std::string s; }; MyStruct ms;你可以通过如下声明直接把两个成员绑定到新的变量名: auto[u,v] ms;这种声明方式就称为结构化绑定。 下面这段代码演示了结构化绑定带来的好…

代码随想录算法训练营day24 || 回溯法原理讲解,77.组合

回溯方法的理论原理与定义 回溯算法是潜藏于递归过程之中一种操作,与递归操作相辅相成;初步理解,有递归必有回溯,使用回溯最好的方式是递归,至于其他的方式有待探索。回溯是一种多重循环的变体,其本质就是…

Qt事件过滤

1.相关说明 监控鼠标进入组件、出组件、点击组件、双击组件的事件,需要重写eventFilter函数 2.相关界面 3.相关代码 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui-&…

数据库(MySQL库表操作)

目录 1.1 SQL语句基础(SQL命令) 1.1.1 SQL的简介 1.1.2 SQL语句的分类 1.1.3 SQL语句的书写规范 1.2 数据库操作 1.2.1 查看 1.2.2 自建库 1.2.3 切换数据库 1.2.4 删库 1.3 MySQL字符集 1.3.1 MySQL字符集包括: 1.3.2 utf8 和 u…

Linux设备管理模型-01:基础数据结构

文章目录 1. 设备管理模型2. 基本数据结构2.1 kobject2.2 kset 1. 设备管理模型 设备模型是内核提供的一个编写驱动的架构。 设备管理是设备-总线-驱动结构。 linux中的设备是由树状模型组织的,从sysfs中可以查看树状结构。 他本身实现了: 电源管理热…

汽车制动器行业调查:市场将继续呈现稳中向好发展态势

汽车制动器是汽车的制动装置,汽车所用的制动器几乎都是摩擦式的,可分为鼓式和盘式两大类。鼓式制动器摩擦副中的旋转元件为制动鼓,其工作表面为圆柱面;盘式制动器的旋转元件则为旋转的制动盘,以端面为工作表面。 目前市场上主流的…

JAVA和C++ SECS/GEM300开发和概念

编译SECS示例程序 1. 示例程序使用默认路径: D:\SECS 稳定版\SECS Debug\ 2. 该操作分为俩步 ① 将C#的Secs库编译成设备相同Net版本。 如.net3.5、4.0、4.5等等 ② 编译金南瓜SECS demo程序 编译C#的SecsEquip.dll 1. 找到SecsEquip项目 项目文件 使用Visua…

JS的作用域链是静态的,它的取值是在创建阶段而不是调用阶段

问题 对于下面这段代码,您觉得会输出什么? var x 10 function fn() {console.log(x) } function show(f) {var x 20;(function () {f() // 10,而不是 20})() } show(fn)由于第8行的f()执行的就是第2行定于你的fn()函数,而第二行…

麒麟V10挂载iso,配置yum源

本文介绍yum 如何挂载本地镜像源 1) 拷贝镜像到本地 2) 执行以下命令: # mount -o loop 镜像路径及镜像名字 /mnt(或 media) 挂载前 挂载后 3) 进入/etc/yum.repos.d(yum.repos.d 是一个目录,该目录是分析 RPM 软件…