Python-多进程共享数据1

python的multiprocessing模块提供了两个方法来共享数据:Value和Array

  • Array: 从共享内存中分配ctypes数组
  • Value: 从共享内存中分配ctypes对象

对於单个数字,字符或者字符串类型的同步,使用multiprocessing中的Value方法即可。

Value(typecode_or_type, *args[, lock])

该方法从共享内存中分配一个ctypes 对象,返回值实际上是对象的同步包装器(synchronized wrapper)。可以通过value属性访问对象本身。其中typecode_or_type定义了分配对象的类型。typecode_or_type可以是一个ctypes类型,或是一个代表ctypes类型的字符代码。具体对应关系见下表

例如,以下两段代码等价

from ctypes import c_int
i=Value(c_int,10)#等价
i=Value('i',10)

示例代码:

from multiprocessing import Process,Value
def f(n):
    n.value=n.value-1
if __name__=='__main__':
    n=Value('i',10)
    print(n.value)
    p=Process(target=f,args=(n,))
    p.start()
    p.join()
    print(n.value)

输出结果

10
9

注意,p.join()如果不写会导致输出结果都为10,因为此时子进程还没有修改变量主进程就输出了n值,加入p.join()后主进程将等待子进程执行完毕后再继续执行

对于多个进程或线程修改共享变量,需要注意“锁”的问题。

举例:

import time
from multiprocessing import Process, Value

def func(val):
    for i in range(50):
        time.sleep(0.01)
        val.value += 1

if __name__ == '__main__':
    v = Value('i', 0)
    procs = [Process(target=func, args=(v,)) for i in range(10)]

    for p in procs: p.start()
    for p in procs: p.join()

    print v.value
最终的value值很可能不是500。
原因如下:multiprocessing.Value的默认锁是非常细粒度的,Value.value表示了内存中的ctypes对象,Value是对ctypes对象的包装,Value能保证只有一个进程或线程能同时读写value属性,这是因为读操作和写操作有时并不是原子操作,也就是说一个写操作可能需要几个CPU指令,而此时另一个进程或线程可能在写操作完成之前读取value值,就会读取错误值,Value能避免这种情况的发生。
但是当我们使用语句
val.value +=1

包含了读操作和写操作,实际执行的是如下指令:

 0 LOAD_FAST                0 (val)
 3 DUP_TOP
                                     #<--- Value lock acquired
 4 LOAD_ATTR                0 (value)
                                     #<--- Value lock released
 7 LOAD_CONST               1 (1)
10 INPLACE_ADD
11 ROT_TWO
                                     #<--- Value lock acquired
12 STORE_ATTR               0 (value)
                                     #<--- Value lock released

也就是说,在读和写操作之间,完全有可能有另一个线程获取锁并且读取错误值,导致最终值不是500。

解决方法比较简单,就是使用一个单独的锁,在该语句执行前加上获取锁的操作:

import time
from multiprocessing import Process, Value, Lock

def func(val, lock):
    for i in range(50):
        time.sleep(0.01)
        with lock:
            val.value += 1

if __name__ == '__main__':
    v = Value('i', 0)
    lock = Lock()
    procs = [Process(target=func, args=(v, lock)) for i in range(10)]

    for p in procs: p.start()
    for p in procs: p.join()

    print v.value

参考:

1.https://eli.thegreenplace.net/2012/01/04/shared-counter-with-pythons-multiprocessing

2.https://docs.python.org/2/library/multiprocessing.html

3.https://www.jb51.net/article/165430.htm

 
发布了18 篇原创文章 · 获赞 7 · 访问量 7231
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章