初始化为全局/局部变量时,多处理锁的工作方式不同

Posted

技术标签:

【中文标题】初始化为全局/局部变量时,多处理锁的工作方式不同【英文标题】:Multiprocessing lock working differently when initialized as global/local variable 【发布时间】:2021-11-22 15:19:16 【问题描述】:

我正在使用多处理锁来确保文件访问多处理安全。根据我在哪里实例化我的锁,我会遇到意外的行为。

在下面的代码中,我有选项 1 和选项 2。根据我最终使用的选项,我会得到不同的结果。选项 1 产生预期的结果,其中锁阻止其他进程执行工作,直到具有锁的进程释放它。另一方面,选项 2 的行为就像锁不存在一样,几乎就像进程之间的锁不共享一样。下面也是两次运行的结果。我什至打印了 ID,但它们在进程之间是不同的值,所以我无法从中获得任何信息。为什么选项 1 能按预期工作,而选项 2 不能?

我正在使用 python 3.7.3

# file1.py
from file2 import ProcessManager

class Program:
    def __init__(self):
        self.manager = ProcessManager()
    def start(self):
        self.manager.run()
if __name__ == "__main__":
    program = Program()
    program.start()

# file2.py
import multiprocessing
from file3 import workingProcess

PACKAGE_LOCK = multiprocessing.Lock() # Option 1: Worked as expected

class ProcessManager:
    def __init__(self):

        self.proc = [] # list of processes

    def run(self):
    
        # package_lock = multiprocessing.Lock() Option 2: Would not work as expected
        
        for i in range(3):
            self.proc.append(
                multiprocessing.Process(
                    target=workingProcess,
                    name=f"Process i",
                    args=(i, PACKAGE_LOCK,) # Option 1: Worked as expected
                    # args=(i, package_lock,) # Option 2: Would not work as expected
                )
            self.proc[-1].start()

        # Do other stuff

# file3.py  
def workingProcess(
    process_idx,
    package_lock
):
    package_lock.acquire()
    print(f"process_idx start time.time()")
    print(f"process_idx ID id(package_lock)")
    # Do Stuff
    print(f"process_idx finished time.time()")
    package_lock.release()

选项 1 结果

0 start 1633029033.4513052
0 ID 3096670642920
0 finished 1633029047.2527368
1 start 1633029047.2537322
1 ID 2665907335800

选项 2 结果:

0 start 1633028808.6572444
0 ID 1627297871128
1 start 1633028810.7597322
1 ID 2176530584688
2 start 1633028813.3802645
2 ID 2811978711784
0 finished 1633028823.7506292

【问题讨论】:

您在这两种情况下都创建了一个Lock 实例但在这两种情况下您都没有使用它(如调用acquirerelease)。 即使start 您创建的进程也没有代码。 请参阅How to create a Minimal, Reproducible Example。另外,请根据 SO 指南使用您正在运行的平台标记您的问题。 @Booboo 抱歉,我没有复制部分代码。固定。 能否添加实例化ProcessManager并调用run()的代码? @quamrana 添加了 run() 的调用位置以及 3 个组件如何通过 python 脚本分隔。 不知道为什么你以daemon 运行子进程,因为一旦你完成创建它们,主进程将退出,并终止所有子进程,我想这显然不是你想要的,所以不要'不了解您发布的输出。对我来说,两个锁在 python 3.8 下都按预期工作 【参考方案1】:

您发布的代码仍然不是Minimal, Reproducible Example;它缺少必需的导入语句,有多个语法错误,例如缺少关闭)multiprocessingmp 的使用不一致,testsuite 未定义等。

我已尝试清理代码,以便您的“选项 2”编译并运行,我认为没有问题(我已将类定义移至源代码的开头):

import multiprocessing as mp
import time

class ProcessManager:
    def __init__(self):

        self.proc = [] # list of processes

    def run(self):

        package_lock = mp.Lock()

        for i in range(3):
            self.proc.append(
                mp.Process(
                    target=workingProcess,
                    name=f"Process i",
                    args=(i, package_lock,) # Option 2: Would not work as expected
                )
            )
            self.proc[-1].start()

def workingProcess(
    process_idx,
    package_lock
):
    package_lock.acquire()
    print(f"process_idx start time.time()")
    print(f"process_idx ID id(package_lock)")
    # Do Stuff
    print(f"process_idx finished time.time()")
    package_lock.release()

class Program:
    def __init__(self):
        self.manager = ProcessManager()
    def start(self):
        self.manager.run()

if __name__ == "__main__":
    program = Program()
    program.start()

打印:

0 start 1633092270.5815103
0 ID 2430872331648
0 finished 1633092270.5845103
1 start 1633092270.5845103
1 ID 1643658290560
1 finished 1633092270.586511
2 start 1633092270.586511
2 ID 2922265716096
2 finished 1633092270.5885122

【讨论】:

以上是关于初始化为全局/局部变量时,多处理锁的工作方式不同的主要内容,如果未能解决你的问题,请参考以下文章

全局变量和局部变量

静态局部变量

在keil中初始化时如何定义一个全局变量,并且赋初值为0xff

小狼,你家BOSS喊你面试啦!!!

静态局部变量

面试八股文