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
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