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程序的方法
Apr 21 Python
python+mysql实现简单的web程序
Sep 11 Python
Python实现求最大公约数及判断素数的方法
May 26 Python
Python 中迭代器与生成器实例详解
Mar 29 Python
python通过pip更新所有已安装的包实现方法
May 19 Python
用python制作游戏外挂
Jan 04 Python
python3将视频流保存为本地视频文件
Jun 20 Python
python用BeautifulSoup库简单爬虫实例分析
Jul 30 Python
深入了解Django View(视图系统)
Jul 23 Python
Django中自定义查询对象的具体使用
Oct 13 Python
Python 3.8正式发布,来尝鲜这些新特性吧
Oct 15 Python
Pytorch之扩充tensor的操作
Mar 04 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/06 新手入门
PHP过滤★等特殊符号的正则
2014/01/27 PHP
你应该知道PHP浮点数知识
2015/05/13 PHP
PHP的serialize序列化数据以及JSON格式化数据分析
2015/10/10 PHP
Symfony2安装第三方Bundles实例详解
2016/02/04 PHP
PHP实现的网站目录扫描索引工具
2016/09/08 PHP
关于laravel后台模板laravel-admin select框的使用详解
2019/10/03 PHP
js获取RadioButtonList的Value/Text及选中值等信息实现代码
2013/03/05 Javascript
jquery基础教程之deferred对象使用方法
2014/01/22 Javascript
浅谈javascript中自定义模版
2015/01/29 Javascript
js如何实现点击标签文字,文字在文本框出现
2015/08/05 Javascript
JS常用函数和常用技巧小结
2016/10/15 Javascript
JS限制条件补全问题实例分析
2016/12/16 Javascript
学习 NodeJS 第八天:Socket 通讯实例
2016/12/21 NodeJs
vue2.0结合DataTable插件实现表格动态刷新的方法详解
2017/03/17 Javascript
angular将html代码输出为内容的实例
2018/09/30 Javascript
vue elementui form表单验证的实现
2018/11/11 Javascript
[04:42]2015国际邀请赛CDEC战队晋级之路
2015/08/13 DOTA
[01:00:25]NB vs Secret 2018国际邀请赛小组赛BO1 B组加赛 8.19
2018/08/21 DOTA
[04:09]2018年度DOTA2社区贡献奖-完美盛典
2018/12/16 DOTA
Python基于Socket实现的简单聊天程序示例
2017/08/05 Python
Python多线程应用于自动化测试操作示例
2018/12/06 Python
使用OpenCV实现仿射变换—旋转功能
2019/08/29 Python
PyTorch实现ResNet50、ResNet101和ResNet152示例
2020/01/14 Python
解决Jupyter NoteBook输出的图表太小看不清问题
2020/04/16 Python
python读取hdfs并返回dataframe教程
2020/06/05 Python
Python 通过爬虫实现GitHub网页的模拟登录的示例代码
2020/08/17 Python
阿里巴巴Oracle DBA笔试题答案-备份恢复类
2013/11/20 面试题
前台文员岗位职责及工作流程
2013/11/19 职场文书
经典公益广告词
2014/03/13 职场文书
竞选班干部演讲稿300字
2014/08/20 职场文书
党员剖析材料范文
2014/09/30 职场文书
教师党员个人总结
2015/02/10 职场文书
如何使用PyCharm及常用配置详解
2021/06/03 Python
浅谈PostgreSQL表分区的三种方式
2021/06/29 PostgreSQL
Redis集群节点通信过程/原理流程分析
2022/03/18 Redis