python multiprocessing多进程变量共享与加锁的实现


Posted in Python onOctober 02, 2019

python多进程和多线程是大家会重点了解的部分,因为很多工作如果并没有前后相互依赖关系的话其实顺序并不是非常的重要,采用顺序执行的话就必定会造成无谓的等待,任凭cpu和内存白白浪费,这是我们不想看到的。

为了解决这个问题,我们就可以采用多线程或者多进程的方式,(多线程我们之后再讲),而这两者之间是有本质区别的。就内存而言,已知进程是在执行过程中有独立的内存单元的,而多个线程是共享内存的,这是多进程和多线程的一大区别。

利用Value在不同进程中同步变量

在多进程中,由于进程之间内存相互是隔离的,所以无法在多个进程中用直接读取的方式共享变量,这时候就可以用multiprocessing库中的 Value在各自隔离的进程中共享变量。

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

假设有一个counter用来记录程序经过的总循环次数,每调用一次count函数之后counter就会增加20,在主程序中用循环开10个进程分别调用count函数,那么理想状态下,在十个进程中共享的counter值到程序结束后应该是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()

运行这个例子,会得到怎样的结果呢?

188
180
168
186
183
179
186
181
166
186

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

相信很多人都已经明白了问题所在,那就是因为在multiprocessing库中的Value是细粒度的,Value中有一个ctypes类型的对象,拥有一个value属性来表征内存中实际的对象。Value可以保证同时只有一个单独的线程或进程在读或者写value值。这么看起来没有什么问题。

然而在第一个进程加载value值的时候,程序却不能阻止第二个进程加载旧的值。两个进程都会把value拷贝到自己的私有内存然后进行处理,并写回到共享值里。

那么这么会出现什么问题呢?

最后的共享值只接收到了一次值的增加,而非两次。

利用Lock在不同进程共享变量时加锁

上面的问题其实可以用一个非常简单的方法解决,我们只需要调用multiprocessing库中的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 相关文章推荐
解决Python 中英文混输格式对齐的问题
Jul 16 Python
python opencv实现旋转矩形框裁减功能
Jul 25 Python
pip安装py_zipkin时提示的SSL问题对应
Dec 29 Python
DataFrame:通过SparkSql将scala类转为DataFrame的方法
Jan 29 Python
从0开始的Python学习016异常
Apr 08 Python
使用pyinstaller打包PyQt4程序遇到的问题及解决方法
Jun 24 Python
浅谈python图片处理Image和skimage的区别
Aug 04 Python
解决django中form表单设置action后无法回到原页面的问题
Mar 13 Python
Python 解决相对路径问题:"No such file or directory"
Jun 05 Python
python爬虫---requests库的用法详解
Sep 28 Python
Django跨域请求原理及实现代码
Nov 14 Python
python 单机五子棋对战游戏
Apr 28 Python
Python shutil模块用法实例分析
Oct 02 #Python
Windows平台Python编程必会模块之pywin32介绍
Oct 01 #Python
Python全栈之列表数据类型详解
Oct 01 #Python
python2和python3应该学哪个(python3.6与python3.7的选择)
Oct 01 #Python
使用Python制作一个打字训练小工具
Oct 01 #Python
Python + Flask 实现简单的验证码系统
Oct 01 #Python
python 矢量数据转栅格数据代码实例
Sep 30 #Python
You might like
中国收音机工业发展史
2021/03/02 无线电
PHP服务器页面间跳转实现方法
2012/08/02 PHP
jQuery中与toggleClass等价的程序段 以及未来学习的方向
2010/03/18 Javascript
防止xss和sql注入:JS特殊字符过滤正则
2013/04/18 Javascript
javascript去掉代码里面的注释
2015/07/24 Javascript
使用JQuery实现的分页插件分享
2015/11/05 Javascript
JavaScript常用字符串与数组扩展函数小结
2016/04/24 Javascript
jQuery插件ajaxFileUpload使用实例解析
2016/10/19 Javascript
原生JS版和jquery版实现checkbox的全选/全不选/点选/行内点选(Mr.Think)
2016/10/29 Javascript
JS数组排序方法实例分析
2016/12/16 Javascript
JavaScript实现二分查找实例代码
2017/02/22 Javascript
使用gulp搭建本地服务器并实现模拟ajax
2017/04/05 Javascript
Vue2.0生命周期的理解
2018/08/20 Javascript
JavaScript常用工具方法封装
2019/02/12 Javascript
JS如何实现动态添加的元素绑定事件
2019/11/12 Javascript
vue 组件开发原理与实现方法详解
2019/11/29 Javascript
[02:20]DOTA2中文配音宣传片
2013/05/22 DOTA
python中实现精确的浮点数运算详解
2017/11/02 Python
利用信号如何监控Django模型对象字段值的变化详解
2017/11/27 Python
Python实现连接postgresql数据库的方法分析
2017/12/27 Python
python基础之包的导入和__init__.py的介绍
2018/01/08 Python
PyQt5每天必学之弹出消息框
2018/04/19 Python
scrapy-redis源码分析之发送POST请求详解
2019/05/15 Python
你可能不熟练的十个前端HTML5经典面试题
2018/07/03 HTML / CSS
Html5内唤醒百度、高德APP的实现示例
2019/05/20 HTML / CSS
生产主管岗位职责
2013/11/10 职场文书
农村文化活动总结
2014/08/28 职场文书
2014党员干部四风问题对照检查材料思想汇报
2014/09/24 职场文书
教师党员自我评议不足范文
2014/10/19 职场文书
督导岗位职责
2015/02/04 职场文书
投资公司董事长岗位职责
2015/04/16 职场文书
2015年车间安全管理工作总结
2015/05/13 职场文书
小学生节约用水倡议书
2019/08/12 职场文书
python opencv人脸识别考勤系统的完整源码
2021/04/26 Python
海贼王十大逆天果实 魂魂果实上榜,岩浆果实攻击力最强
2022/03/18 日漫
PostgreSQL聚合函数介绍以及分组和排序
2022/04/12 PostgreSQL