Python 修复共享内存问题和锁定共享资源问题

news2024/12/25 2:29:39

文章目录

    • 使用 multiprocessing.Array() 在 Python 中使用共享内存
      • 解决多进程之间共享数据问题的解决方案
    • 使用 multiprocessing.Lock() 锁定 Python 中的共享资源


本篇文章解释了多处理共享内存的不同方面,并演示了如何使用共享内存解决问题。 我们还将学习如何使用锁来锁定 Python 中的共享资源。


使用 multiprocessing.Array() 在 Python 中使用共享内存

当您有多个子进程时,多处理最关键的方面之一是在进程之间共享数据。

您使用处理模块的能力创建的子进程的基本属性之一是它们独立运行并拥有自己的内存空间。

这意味着孩子的进程将有一些内存空间。 并且,任何变量试图在其自己的内存空间中创建或将被更改,而不是在其父级的内存空间中。

让我们通过一个例子来尝试理解这个概念,并通过导入多处理模块进入代码。

我们创建了一个名为 RESULT 的空列表,并定义了一个名为 Make_Sqaured_List() 的函数,该函数对给定列表的元素进行平方并将它们附加到我们的全局 RESULT 列表中。

Procc_1 对象等于 Process() 类,并将目标设置为不带括号的 Make_Sqaured_List 函数。

并且,对于 args 参数,我们传递一个名为 Items_list 的单独列表,该列表将作为参数提供给 Make_Sqaured_List() 函数。

示例代码:

import multiprocessing

RESULT = []
def Make_Sqaured_List(Num_List):
    global RESULT

    for n in Num_List:
        RESULT.append(n **2)
    print(f"Result: {RESULT}")

if __name__ == '__main__':
    Items_list = [5,6,7,8]

    Procc_1 = multiprocessing.Process(target=Make_Sqaured_List, args=(Items_list,))
    Procc_1.start()
    Procc_1.join()

让我们执行这个子进程,我们根据我们的子进程得到的结果是全局列表的值。

使用 multiprocessing.Array() 在 Python 中使用共享内存

但是,如果我们尝试打印仍然为空的 RESULT 列表,那么 RESULT 列表会发生什么情况?

print(RESULT)

输出:

[]

从我们的主进程来看,我们的父进程还是空的,而从子进程来看,RESULT列表是有内容的。 这只是意味着我们的不同进程都有不同的内存空间。

我们可以通过一个场景来理解这一点,在这个场景中我们有一个进程,这是我们的主程序,我们最初有一个空的 RESULT 列表。

而且,当我们创建一个子进程时,它最初也是一个空的,然后执行 Make_Sqaured_List() 函数,因此 RESULT 列表包含一些项目。 但是,由于我们正在从该内存空间中的父进程访问 RESULT,因此更改是不可见的。

示例代码:

import multiprocessing

RESULT = []
def Make_Sqaured_List(Num_List):
    global RESULT

    for n in Num_List:
        RESULT.append(n **2)
    print(f"Result: {RESULT}")

if __name__ == '__main__':
    Items_list = [5,6,7,8]

    Procc_1 = multiprocessing.Process(target=Make_Sqaured_List, args=(Items_list,))
    Procc_1.start()
    Procc_1.join()
    print(RESULT)

输出:

使用 multiprocessing.Array() 在 Python 中使用共享内存 2

那么,解决这个问题的方法是什么? 但是,首先,让我们看看如何解决它。

解决多进程之间共享数据问题的解决方案

在本节中,我们将看到帮助我们获取任何更改的值并解决多进程之间共享数据问题的解决方案。

该解决方案称为共享内存。 多处理模块为我们提供了两种类型的对象,称为数组和值,可用于在进程之间共享数据。

Array是从共享内存中分配的数组; 基本上,您的计算机内存中有一部分我们可以称为共享内存或多个进程可以访问的区域。

因此,在该共享内存中,我们创建了一个新数组或一个新值。 这些附加值不是我们的基本 Python 数据结构; multiprocessing 模块本身有一些不同和定义的东西。

现在我们使用 multiprocessing.Array() 声明一个名为 RESULT_ARRAY 的对象。 然后,在这个数组中,我们必须传递数据类型。 我们将 i 作为字符串传递,这意味着我们将在其中放入整数值,并且我们必须给出大小。

示例代码:

RESULT_ARRAY= multiprocessing.Array('i', 4)

它与 C 编程风格的数组有关,因此我们可以同时给出大小。 这样,我们就可以将对象存储在所需的位置。

现在我们正在创建一个名为 OBJ_Sum 的新值,它等于 multiprocessing.Value(),它将存储并输入该值。

示例代码:

OBJ_Sum = multiprocessing.Value('i')

接下来,我们将创建一个名为 procc_1 的对象,它将等于 multiprocessing.Process(),我们将调用一个函数。 我们创建了一个名为 Make_Sqaured_List() 的函数,它将采用三个参数:

  • list
  • array 对象
  • value 对象。

我们将使用名为 args 的 Process 参数将这三个参数传递给我们的函数。 例如,看看下面的代码片段。

示例代码:

Procc_1 = multiprocessing.Process(target=Make_Sqaured_List, args=(Items_list, RESULT_ARRAY, OBJ_Sum))

现在在 Make_Sqaured_List() 函数中,我们将使用 enumerate() 函数迭代 Items_list。 这样我们就可以得到Items_list的索引和值了。

它是一个 C 风格的数组,所以我们必须使用索引将值分配给我们的数组。 我们还将对数组的值求和,OBJ_Sum.value 是 multiprocessing.Value() 的一个属性。

示例代码:

def Make_Sqaured_List(Items_list, RESULT, OBJ_Sum):

    for i, n in enumerate(Items_list):
        RESULT[i] = n **2

    OBJ_Sum.value = sum(RESULT)

我们在主进程中定义了一些变量,改变了子进程调用的函数。 所以我们的主要议程是我们是否可以在我们的主要流程中获得那些改变的价值。

现在我们可以访问反映在子进程中的数组并使用 OBJ_Sum.value 获取其总和。 例如,请参见以下代码片段。

示例代码:

import multiprocessing

def Make_Sqaured_List(Items_list, RESULT, OBJ_Sum):

    for i, n in enumerate(Items_list):
        RESULT[i] = n **2

    OBJ_Sum.value = sum(RESULT)

if __name__ == '__main__':
    Items_list = [5,6,7,8]

    RESULT_ARRAY= multiprocessing.Array('i', 4)
    OBJ_Sum = multiprocessing.Value('i')

    Procc_1 = multiprocessing.Process(target=Make_Sqaured_List, args=(Items_list, RESULT_ARRAY, OBJ_Sum))
    Procc_1.start()
    Procc_1.join()
    print(RESULT_ARRAY[:])
    print(OBJ_Sum.value)

输出:

解决多进程之间共享数据问题的解决方案

这样,我们可以对父进程中定义的对象以及我们从子进程返回的那些更改进行任何更改。 这可以通过使用共享内存技术来实现。


使用 multiprocessing.Lock() 锁定 Python 中的共享资源

我们将讨论一个名为锁的重要主题; 现在,如果您上过计算机科学或操作系统课程,那么您已经了解了锁。 但是,当涉及到多处理和操作系统概念时,锁是一个关键概念。

首先,让我们考虑一下现实生活中为什么需要锁; 在我们的日常生活中,有些资源不能被两个人同时访问。

例如,浴室的门有锁,因为如果两个人同时试图打开它,就会造成相当尴尬的局面。 这就是我们保护浴室的原因,这是一个带锁的共享资源。

同样,在编程世界中,每当两个进程或线程试图访问一个共享资源,例如共享内存文件或数据库时。 它会产生问题,因此您必须使用锁来保护该访问。

如果我们不将这种保护添加到我们的程序中会发生什么,我们将通过运行示例来了解。 同样,这是一个银行软件程序,这里有两个进程。

第一个过程是使用 MONEY_DP() 函数将钱存入银行,第二个过程是使用 MONEY_WD() 函数从银行取款。 最后,我们正在打印最终余额。

我们从 MONEY_DP 部分的 200 美元余额开始。 我们存入 100 美元,我们有一个跟踪 100 次的 for 循环,在每次迭代中,它都会将 01 美元添加到我们的银行账户中。

同样,在 MONEY_WD 函数中,我们将相同的循环迭代 100 次,每次都会从我们的银行账户中扣除 1 美元。

示例代码:

import multiprocessing
import time

def MONEY_DP(B):
    for i in range(100):
        time.sleep(0.01)
        B.value = B.value + 1

def MONEY_WD(B):
    for i in range(100):
        time.sleep(0.01)
        B.value = B.value - 1

现在我们正在使用一个名为 value 的共享内存变量,我们已经在上一节中了解过它。 这个 multiprocessing 值是一个共享内存资源,所以让我们看看当我们尝试运行这个程序时会发生什么。

if __name__ == '__main__':
    B = multiprocessing.Value('i', 200)
    Deposit = multiprocessing.Process(target=MONEY_DP, args=(B,))
    Withdrawl = multiprocessing.Process(target=MONEY_WD, args=(B,))
    Deposit.start()
    Withdrawl.start()
    Deposit.join()
    Withdrawl.join()
    print(B.value)

我们将多次运行它,每次,它只是打印不同的值,但它应该打印 200 美元。

输出:

# execution 1
205
# execution 2
201
# execution 3
193

为什么会这样? 它主要发生在该进程试图读取共享内存中名为 B.value 的变量时。

假设 B.value 有一个 200$ 的值,它会读取它,然后加一,然后它会把同样的东西放回同一个变量中。

由于 B.value 是 200$ 并且它是在操作系统级别执行此加法操作,因此在操作系统级别,它将执行多个流水线指令。

所以我们读取了变量 200; 它被加一,并将 201 分配回 B.value 变量。

示例代码:

B.value = B.value + 1

现在,当它同时执行时,该指令也在 MONEY_WD() 函数中执行。

示例代码:

B.value = B.value - 1

虽然我们是先存后取,但是当进程试图读取B.value时,它仍然是200,因为存入过程还没有写回原来的变量。

而不是在 MONEY_WD 进程中将 B.value 读取为 201,它将读取 B.value 为 200,并且在减少 1 之后,它将有 199。

这就是为什么我们会出现不一致的行为。 首先,让我们使用 Lock 来锁定访问; 现在,我们创建一个名为 lock 的变量,并使用多处理模块来使用 Lock 类。

示例代码:

lock=multiprocessing.Lock()

现在我们将该锁传递给两个进程,在两个进程内,我们将调用 lock.acquire() 来放置锁,然后释放锁,我们将调用 lock.release() 函数。

这些锁函数在访问共享资源时保护代码段,称为临界区。

示例代码:

import multiprocessing
import time

def MONEY_DP(B,lock):
    for i in range(100):
        time.sleep(0.01)
        lock.acquire()
        B.value = B.value + 1
        lock.release()
def MONEY_WD(B,lock):
    for i in range(100):
        time.sleep(0.01)
        lock.acquire()
        B.value = B.value - 1
        lock.release()
if __name__ == '__main__':
    B = multiprocessing.Value('i', 200)
    lock=multiprocessing.Lock()
    Deposit = multiprocessing.Process(target=MONEY_DP, args=(B,lock))
    Withdrawl = multiprocessing.Process(target=MONEY_WD, args=(B,lock))
    Deposit.start()
    Withdrawl.start()
    Deposit.join()
    Withdrawl.join()
    print(B.value)

现在,这段代码每次都打印 200。

输出:

200
PS C:\Users\Dell\Desktop\demo> python -u "c:\Users\Dell\Desktop\demo\demo.py"
200
PS C:\Users\Dell\Desktop\demo> python -u "c:\Users\Dell\Desktop\demo\demo.py"
200

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

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

相关文章

Axure教程—图片手风琴效果

本文将教大家如何用AXURE制作图片手风琴效果 一、效果介绍 如图: 预览地址:https://6nvnfm.axshare.com 下载地址:https://download.csdn.net/download/weixin_43516258/87847313?spm1001.2014.3001.5501 二、功能介绍 图片自动播放为手风…

MT4交易外汇平台有哪些优势?为何是外汇投资首选?

外汇市场上存在着各种各样的外汇交易商,但是很多的外汇交易商所选择的交易平台都是MT4交易外汇平台。作为全世界范围内使用最为广泛的交易平台,MT4交易外汇平台具有哪些优势,能够让外汇交易商和外汇投资者都选择使用。本文就来具体的聊聊&…

SQL中not in的一个坑

因not in 效率较低,在工作用一只用left join代替,在某一次查询使用了not in发现,结果为空,sql大致如下 select id from table1 where id not in (select id from table2)经过查询发现select id from table2里面的id有null值导致该…

司法大数据解决方案

2018年11月26日,司法部制定了《智慧监狱技术规范SFT0028-2018》并于2019年1月1日正式颁布实施,要求智慧监狱的建设应者眼于监狱工作实际,将物联网、云计算、大数据、人工智能等新一信息技术与监狱各项业务深度融合,形成标准规范科…

论文解读 | 基于改进点对特征的点云6D姿态估计

原创 | 文 BFT机器人 01 摘要 点对特征(PPF)方法已被证明是一种有效的杂波和遮挡下的姿态估计方法。 文章的改进方法主要包括: (1)一种基于奇偶规则求解封闭几何的法向的方法; (2)通过将体素网格划分为等效角度单元的有效降采样方法; (3)基于拟合点的验证步骤。在真实杂波数据集…

如何挖掘360下拉词,怎么删除360下拉负面词

大多数人的手机或者电脑上都安装了360浏览器,360搜索APP等,安装的用户量大,自然使用的用户也就多了,360下拉的优势就展现出来了,展示量大,全国各地均可见;能够引流来的都是相对精准的目标用户&a…

C#简单数据结构类和常用泛型结构类

文章目录 1.简单数据结构类(1)动态数组Arraylist(2)栈Stack(3)队列Queue(4)哈希表Hashtable 2.泛型3.常用泛型数据结构类(1)列表List(2&#xff0…

高完整性系统:Fault Tolerant Design

目录 1. INTRODUCTION TO FAULT TOLERANCE 1.2 Definitions 1.3 Two Kinds of Faults 1.4 Hardware vs Software Faults 1.4.1 Failure Curve for Hardware 1.4.2 Hardware and Software Failures 1.5 Causes of Failures 1.6 3 Ways to Class Failures 1.6.1 Tempora…

Java 岗史上最全八股文面试真题汇总,堪称 2023 年面试天花板

前言 现如今,Java 面试的本质就是八股文,把八股文面试题背好,面试才有可能表现好。金九银十招聘黄金季已经来临!大家在考研和找工作中纠结的时候,不妨先看一下面试题,毕竟我们的最终目标就是为了找一份心仪…

机器视觉怎么对陶瓷板外观尺寸进行自动检测?

随着陶瓷行业的发展,陶瓷板的生产和质量控制面临越来越高的要求。而机器视觉技术作为一种高精度、高效率、无损、可靠性高的自动化检测手段,已经成为陶瓷板外观尺寸自动化检测的首选方案。本文就如何利用机器视觉对陶瓷板外观尺寸进行自动检测进行分析和…

配电室的管理制度及综合监控系统的介绍

安科瑞虞佳豪 1、配电室全部机电设备,由配电室人员负责管理和值班,停送电由值班电工操作,非值班电工禁止操作,无关人员禁止进入配电室;公司内有关上级部门因检查工作,必须要进入这些场所时,应由…

【温故而知新】阶段总结!我在技术成长过程中的收获!

时间:2023年05月31日 作者:小蒋聊技术 邮箱:wei_wei10163.com 微信:wei_wei10 【20230531】【温故而知新】阶段总结!我在技术成长过程中的收获!_小蒋聊技术_免费在线阅读收听下载 - 喜马拉雅手机版欢迎…

第十八章行为性模式—观察者模式

文章目录 观察者模式解决的问题结构实例存在的问题使用场景 JDK 提供的实现 - Observable示例 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,它涉及算法与对象间职责的分配。行…

WMI系列--WMI订阅事件

前边对于WMI的基础内容进行简单的总结和整理,结下来的这篇内容主要针对WMI的永久订阅事件展开详细的阐述。 WMI事件订阅机制 WMI事件分为两类,分别是本地事件订阅和永久性事件订阅。 所谓本地事件是指运行在本地上下文环境当中的单个进程的事件&#x…

入门编程的方法和步骤

编程是信息时代必备的一项技能,无论是从事计算机行业的人员,还是从事其他行业的人员,学会编程对个人职业发展都有着重要的意义。但是,对于初学者来说,如何入门编程往往是一个比较棘手的问题。本文将介绍一些入门编程的…

训练YOLOv5对象检测模型的逐步指导

介绍 欢迎来到我们的 YOLOv5 教程系列的第 2 部分!如果您还没有查看本系列的第 1 部分,我建议您先阅读该部分;它涵盖了如何在 Windows 和 Google Colab 上安装用于真实对象检测的 YOLOv5 ,我们假设您已在本报告中完成了这些操作。 也就是说,一旦您设置了环境,您就可以开…

DuDuTalk语音工牌:语音数据分析在销售场景的应用价值

在现今这个数字时代,企业需要更高效、更有效地沟通和合作。语音数据的收集和分析能够增加销售团队和客户之间的联系,同时提高销售闭合率。因此,了解和利用销售沟通语音数据的价值,是现代企业所必须的。 销售沟通语音数据指的是在…

精益生产管理的优势特点以及工具步骤

一、何为精益生产 精益生产(LeanProduction,简称LP)是美国麻省理工学院数位国际汽车计划组织(IMVP)的专家对日本“丰田JIT(JustInTime)生产方式”的赞誉之称,精,即少而精…

不同股指期货交易平台的优缺点比较,让你一目了然!

股指期货交易平台是股指期货交易的重要场所,是期货市场的核心平台之一。在股指期货交易平台上,投资者可以进行股指期货的买卖,获得相应的投资收益。然而,对于大部分投资者来说,如何选择一个好的股指期货交易平台却是一…

locust压测脚本文档组织结构

以下为locust压测脚本文档组织结构模板,和TestDeploy对接丝滑,轻松实现分布式压测。 1、common common主要是存放改写了的请求方法,包括GET、POST等等,作为统一入口,以便各个API统一调用。 2、config config主要存…