深入理解Python多进程与多线程:内存管理与变量同步的关键区别

Python多进程和多线程是大家都会关注的部分,因为如果很多任务之间没有相互依赖的话,顺序并不是很重要。如果采用顺序执行,肯定会造成不必要的等待,白白浪费CPU和内存。浪费,这是我们不愿意看到的。

深入理解Python多进程与多线程:内存管理与变量同步的关键区别

为了解决这个问题,我们可以采用多线程或者多进程的方式(稍后我们会讲到多线程),两者是有本质区别的。就内存而言,众所周知,进程在执行时拥有独立的内存单元,而多个线程则共享内存。这是多进程和多线程的一个主要区别。

使用Value来同步不同进程中的变量

在多进程中,由于进程之间的内存是相互隔离的,因此变量不能通过直接读取的方式在多个进程之间共享。这时,就可以使用multiprocessing库中的Value来共享各个隔离进程中的变量。

下面是一个多进程的例子:

假设有一个计数器,用来记录程序经过的循环总数。每次调用count函数后,计数器都会增加20。在主程序中,使用一个循环打开10个进程,分别调用count函数。那么理想情况下,在十个进程中共享的计数器值在程序结束后应该是200。

from multiprocessing import Process, Value
import time
def count(v):
    for i in range(20):
        time.sleep(0.01)
        v.value += 1
def main():
    value = Value('i',0)
    processes = [Process(target=count, args=(value,)) for i in range(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print(value.value)
if __name__ == '__main__':
    for i in range(10):
        main()

运行这个例子会得到什么结果?

我在主程序中运行了这个程序十次,最终结果在160-180之间。总之,一直没有达到200,这是什么原因呢?

相信很多人都明白了这个问题。这是因为多处理库中的值是细粒度的。 Value中有一个ctypes类型的对象,还有一个value属性来代表内存中的实际对象。 Value 确保只有一个线程或进程同时读取或写入该值。似乎没有问题。

但是,当第一个进程加载该值时,程序无法阻止第二个进程加载旧值。两个进程都会将该值复制到自己的私有内存中,对其进行处理,然后将其写回共享值。

那么问题出在哪里呢?

最终的共享值仅收到一个增量,而不是两个。

当变量被不同进程共享时,使用Lock来锁定变量

上述问题其实可以通过一个非常简单的方法来解决。我们只需要在多处理库中调用Lock来确保同一时刻只有一个进程可以访问这个共享变量。修改后的代码如下:

from multiprocessing import Process, Value, Lock
from time import sleep
def count(x,lock):
    for i in range(20):
        sleep(0.01)
        with lock:
            x.value += 1
def main():
    counter = Value('i',0)
    lock = Lock()
    processes = [Process(target=count,args=(counter,lock)) for i in range(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print(counter.value)
if __name__ == '__main__':
    for i in range(10):
        main()

这样,输出结果就恒定为200。

一些补充 1.调用get_lock()函数

事实上,Value包中已经包含了锁的概念。如果调用get_lock()函数,可以自动锁定共享变量。这实际上是推荐的方式,因为它不需要同时调用两个包。修改如下:

from multiprocessing import Process, Value
from time import sleep
def count(x):
    for i in range(20):
        global counter # 声明全局变量
        sleep(0.01)
        with counter.get_lock(): # 直接调用get_lock()函数获取锁
            x.value += 1
def main():
    processes = [Process(target=count, args=(counter,)) for i in range(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print(counter.value)
if __name__ == '__main__':
    counter = Value('i', 0) # 需要把全局变量移到主程序
    main()

上面的程序比较明确,最终的结果也是200。

2.使用multiprocessing.RawValue

整个multiprocessing包中刚才调用的Value和Lock也可以用multiprocessing.RawValue来代替。

你可能想看:
« 上一篇
下一篇 »

相关推荐

python爬虫教程PDF,Python爬虫书PDF

2026年09月02日

30阅读

外国教程python,python编程教学视频

2026年09月02日

24阅读

python简明教程沈洁元,python详细基础教程

2026年09月02日

21阅读

python库包教程,python 包

2026年09月02日

26阅读

python教程大全集,python教程600集

2026年09月02日

26阅读

python入门教程完整版,python零基础入门全套教程视频

2026年09月02日

22阅读