今天同事反映一个问题让帮忙看一下:多进程共用一个变量,在一个进程中修改后,在另外的进程中并没有产生修改。

一、错误的实现方式

最初以为是没添加global声明导致修改未生效,但实际操作发现global方式在多进程中也只能读不能写。错误示例代码如下:

  1. import multiprocessing
  2.  
  3. # 声明一个全局变量
  4. share_var = ["start flag"]
  5.  
  6. def sub_process(process_name):
  7. # 企图像单个进程那样通过global声明使用全局变量
  8. global share_var
  9. share_var.append(process_name)
  10. # 但是很可惜,在多进程中这样引用只能读,修改其他进程不会同步改变
  11. for item in share_var:
  12. print(f"{process_name}-{item}")
  13. pass
  14.  
  15. def main_process():
  16. process_list = []
  17. # 创建进程1
  18. process_name = "process 1"
  19. tmp_process = multiprocessing.Process(target=sub_process,args=(process_name,))
  20. process_list.append(tmp_process)
  21. # 创建进程2
  22. process_name = "process 2"
  23. tmp_process = multiprocessing.Process(target=sub_process, args=(process_name,))
  24. process_list.append(tmp_process)
  25. # 启动所有进程
  26. for process in process_list:
  27. process.start()
  28. for process in process_list:
  29. process.join()
  30.  
  31. if __name__ == "__main__":
  32. main_process()

执行结果如下,可以看到进程1中的修改未表现在进程2中(不过要注意,和多线程一样,如果运算量再大一点进程1并不一定比进程2先执行):

二、共享普通类型变量实现方法

参考:https://blog.csdn.net/houyanhua1/article/details/78244288

  1. import multiprocessing
  2.  
  3. # 不能将共享变量和共享锁定义成全局变量然后通过global引用那样会报错,只能传过来
  4. def sub_process(process_name,share_var,share_lock):
  5. # 获取锁
  6. share_lock.acquire()
  7. share_var.append(process_name)
  8. # 释放锁
  9. share_lock.release()
  10. for item in share_var:
  11. print(f"{process_name}-{item}")
  12. pass
  13.  
  14. def main_process():
  15. # 单个值声明方式。typecode是进制类型,C写法和Python写法都可以,见下方表格;value是初始值。
  16. # 这种单值形式取值赋值需要通过get()/set()方法进行,不能直接如一般变量那样取值赋值
  17. # share_var = multiprocessing.Manager().Value(typecode, value)
  18. # 数组声明方式。typecode是数组变量中的变量类型,sequence是数组初始值
  19. # share_var = multiprocessing.Manager().Array(typecode, sequence)
  20. # 字典声明方式
  21. # share_var = multiprocessing.Manager().dict()
  22. # 列表声明方式
  23. share_var = multiprocessing.Manager().list()
  24. share_var.append("start flag")
  25. # 声明一个进程级共享锁
  26. # 不要给多进程传threading.Lock()或者queue.Queue()等使用线程锁的变量,得用其进程级相对应的类
  27. # 不然会报如“TypeError: can't pickle _thread.lock objects”之类的报错
  28. share_lock = multiprocessing.Manager().Lock()
  29. process_list = []
  30.  
  31. process_name = "process 1"
  32. tmp_process = multiprocessing.Process(target=sub_process,args=(process_name,share_var,share_lock))
  33. process_list.append(tmp_process)
  34.  
  35. process_name = "process 2"
  36. tmp_process = multiprocessing.Process(target=sub_process, args=(process_name,share_var,share_lock))
  37. process_list.append(tmp_process)
  38.  
  39. for process in process_list:
  40. process.start()
  41. for process in process_list:
  42. process.join()
  43.  
  44. if __name__ == "__main__":
  45. main_process()

执行结果如下,可以看到进程1中的修改已表现在进程2中(不过要注意,和多线程一样,如果运算量再大一点进程1并不一定比进程2先执行):

typecode如果是数值或单个字符,可为以下类型(注意有引号):

Type Code C Type Python Type
'c' char character
'b' signed char int
'B' unsigned char int
'u' Py_UNICODE unicode character
'h' signed short int
'H' unsigned short int
'i' signed int int
'I' unsigned int int
'l' signed long int
'L' unsigned long int
'f' float float
'd' double float

如果是字符串类型,typecode可为以下第一列形式(注意无引号):

ctypes type C type Python type

c_bool

_Bool bool (1)
char  char 1-character string
c_wchar wchar_t 1-character unicode string
c_byte char int/long
c_ubyte unsigned char int/long
c_short short int/long
c_ushort unsigned short int/long
c_int int int/long
c_uint unsigned in int/long
c_long long int/long
c_ulong unsigned long int/long
c_longlong __int64 or long long int/long
c_ulonglong unsigned __int64 or unsigned long long int/long
c_float float float
c_double double float
c_longdouble long double float
c_char_p char * (NUL terminated) string or None

c_wchar_p

wchar_t * (NUL terminated) unicode or None
c_void_p void * int/long or None

三、共享实例化对象实现方法

同事还想共享一个文件对象,然后问上边的方法是不是只能共享字典、列表,没法共享对象。

回头一看,Value和Array中typecode要求是c语言中存在的类型,其他只有dict()和list()方法没有其他方法,所以似乎上边的方法共享实例化对象是不行的。

3.1 共享不需要修改实例化对象实现方法----使用global

但我们前面说过global方式不可以修改,但读还是没问题的;所以对象引用还是可以使用global方式。

  1. import multiprocessing
  2. import threading
  3.  
  4. # 实例化一个全局文件对象
  5. file_obj = open("1.txt","a")
  6. share_lock = threading.Lock()
  7.  
  8. def sub_process(process_name):
  9. global file_obj,share_lock
  10. share_lock.acquire()
  11. file_obj.writelines(f"{process_name}")
  12. share_lock.release()
  13. pass
  14.  
  15. def main_process():
  16. process_list = []
  17. # 创建进程1
  18. process_name = "process 1"
  19. tmp_process = multiprocessing.Process(target=sub_process,args=(process_name,))
  20. process_list.append(tmp_process)
  21. # 创建进程2
  22. process_name = "process 2"
  23. tmp_process = multiprocessing.Process(target=sub_process, args=(process_name,))
  24. process_list.append(tmp_process)
  25. # 启动所有进程
  26. for process in process_list:
  27. process.start()
  28. for process in process_list:
  29. process.join()
  30.  
  31. if __name__ == "__main__":
  32. main_process()

3.2 共享需要修改实例化对象实现方法----使用BaseManager

global方式不能修改变量(如要修改其成员变量),在大多时候也是可以了,但总让人觉得不是一种完美的实现方法。有没有可以修改的实现方法呢,答案是有的,可以使用BaseManager。示例代码如下。

参考:https://blog.csdn.net/jacke121/article/details/82658471

  1. import multiprocessing
  2. from multiprocessing.managers import BaseManager
  3. import threading
  4.  
  5. # 锁可以通过global也可以在Process中传无所谓
  6. share_lock = threading.Lock()
  7.  
  8. # 定义一个要共享实例化对象的类
  9. class Test():
  10. def __init__(self):
  11. self.test_list = ["start flag"]
  12.  
  13. def test_function(self,arg):
  14. self.test_list.append(arg)
  15.  
  16. def print_test_list(self):
  17. for item in self.test_list:
  18. print(f"{item}")
  19.  
  20. def sub_process(process_name,obj):
  21. global share_lock
  22. share_lock.acquire()
  23. obj.test_function(f"{process_name}")
  24. share_lock.release()
  25. obj.print_test_list()
  26. pass
  27.  
  28. def main_process():
  29. # 如果是想注册open方法这样操作
  30. # manager = BaseManager()
  31. # # 一定要在start前注册,不然就注册无效
  32. # manager.register('open', open)
  33. # manager.start()
  34. # obj = manager.open("1.txt","a")
  35.  
  36. # 为了更加直接我们直接以一个Test类的实例化对象来演示
  37. manager = BaseManager()
  38. # 一定要在start前注册,不然就注册无效
  39. manager.register('Test', Test)
  40. manager.start()
  41. obj = manager.Test()
  42.  
  43. process_list = []
  44. # 创建进程1
  45. process_name = "process 1"
  46. tmp_process = multiprocessing.Process(target=sub_process,args=(process_name,obj))
  47. process_list.append(tmp_process)
  48. # 创建进程2
  49. process_name = "process 2"
  50. tmp_process = multiprocessing.Process(target=sub_process, args=(process_name,obj))
  51. process_list.append(tmp_process)
  52. # 启动所有进程
  53. for process in process_list:
  54. process.start()
  55. for process in process_list:
  56. process.join()
  57.  
  58. if __name__ == "__main__":
  59. main_process()

执行结果如下,可以看到进程1中的修改已表现在进程2中(不过要注意,和多线程一样,如果运算量再大一点进程1并不一定比进程2先执行):

参考:

https://blog.51cto.com/11026142/1874807

https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing

Python3多进程共享变量实现方法的更多相关文章

  1. Python3 多进程编程 - 学习笔记

    Python3 多进程编程(Multiprocess programming) 为什么使用多进程 具体用法 Python多线程的通信 进程对列Queue 生产者消费者问题 JoinableQueue ...

  2. 【python】多进程共享变量

    有一个字典变量,需要在多个进程间共享 使用Manager, 下面是一个小例子. 注意使用json前需要将类型转换. #!/usr/bin/python # coding=utf-8 import js ...

  3. python3打包成exe---pyinstaller方法

    前言: 主要介绍python3的pyinstaller打包方法 pyinstaller安装参考地址:http://www.pyinstaller.org/ pywin32的下载地址:https://s ...

  4. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  5. python2.x脚本转换为python3.x脚本的方法

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/dushu990/article/details/73549174 python2.x脚本转换为pyt ...

  6. Python3 多进程和多线程

    Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为 ...

  7. python多进程共享变量Value使用tips

    前言: 在使用tornado的多进程时,需要多个进程共享一个状态变量,于是考虑使用multiprocessing.Value(对于该变量的具体细节请查阅相关资料).在根据网上资料使用Value时,由于 ...

  8. python3.x pool.map方法的实质

    我使用多进程的一般方式,都是multiprocessing模块中的Pool.map()方法.下面写一个简单的示例和解析.至于此种方法使用多进程的效率问题,还希望大佬予以指正. 示例: "&q ...

  9. 【python】多进程共享变量Manager

    Manager的复杂结构赋值问题 Manager的字典类型: 如果value是简单类型,比如int,可以直接赋值给共享变量,并可以后续直接修改 如果value是复杂类型 ,比如list,dict,则必 ...

随机推荐

  1. mac上使用Sequel Pro工具SSH连接数据库

    今天在使用Mac上的Sequel Pro连接线上的数据库时,一直报ssh通道连接失败.但是同样的公钥在另一台机器就可以,真是奇怪. 通过查找日志发现有一个关键字"key_load_publi ...

  2. open live writer安装以及代码高亮、折叠插件安装

    一.目的 方便在本地写博客,不用在浏览器上写. 二.open live writer的安装 下载open live writer 这是我的 链接:https://pan.baidu.com/s/1u8 ...

  3. Bootstrap初始化过程源码分析--netty客户端的启动

    Bootstrap初始化过程 netty的客户端引导类是Bootstrap,我们看一下spark的rpc中客户端部分对Bootstrap的初始化过程 TransportClientFactory.cr ...

  4. Python 字符串多替换时性能基准测试

    结论 先说结果, 直接替换是最好的. replace 一层层用, 方法笨了一点, 还可以. 时间消耗: tx2 < tx3 < tx1 < tx4 t2 < t3 < t ...

  5. 【Thinkphp】引入第三方类库常见问题

    TP3.2在添加第三方sdk的时候,文件放在ThinkPHP/Library/Org文件夹下可独立创建文件夹(官方文档有其他思路)需对文件做以下修改. 1.第一应该修改文件的名称(下载的sdk一般是 ...

  6. Windows实时预览markdown

    1.安装Notepad++ 2.打开Notepad++菜单栏的"插件(P)",然后打开"插件管理" 3.搜索"MarkdownViewer++&quo ...

  7. c#执行sql超时

    超时分为多种,SqlConnection有超时选项, SqlDataAdapter也有超时选项设置如下: SqlConnection:就用链接字符串给予的Timeout设置就行单位秒: SqlData ...

  8. druid + mysql + mybatis 批量更新报错

    首先 批量更新报错 sql injection violation, multi-statement not allow 然后看了博客:https://blog.csdn.net/qq_3634595 ...

  9. SpringBoot整合自定义FTP文件连接池

    说明:通过GenericObjectPool实现的FTP连接池,记录一下以供以后使用环境:JDK版本1.8框架 :springboot2.1文件服务器: Serv-U1.引入依赖 <!--ftp ...

  10. msyql常用命令

    1.创建.删除数据库 create database dbname; drop database dbname; 2.选择某一个数据库 use dbname; 3.显示所有表 show tables; ...