处理python中多线程与多进程中的数据共享问题


Posted in Python onJuly 28, 2019

之前在写多线程与多进程的时候,因为一般情况下都是各自完成各自的任务,各个子线程或者各个子进程之前并没有太多的联系,如果需要通信的话我会使用队列或者数据库来完成,但是最近我在写一些多线程与多进程的代码时,发现如果它们需要用到共享变量的话,需要有一些注意的地方

多线程之间的共享数据

标准数据类型在线程间共享

看以下代码

#coding:utf-8
import threading
def test(name,data):
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data,id(data)))
if __name__ == '__main__':
  d = 5
  name = "杨彦星"
  for i in range(5):
    th = threading.Thread(target=test,args=(name,d))
    th.start()

这里我创建一个全局的int变量d,它的值是5,当我在5个线程中调用test函数时,将d作为参数传进去,那么这5个线程所拥有的是同一个d吗?我在test函数中通过 id(data) 来打印一下它们的ID,得到了如下的结果

in thread <Thread(Thread-1, started 6624)> name is 杨彦星
data is 5 id(data) is 1763791776
in thread <Thread(Thread-2, started 8108)> name is 杨彦星
data is 5 id(data) is 1763791776
in thread <Thread(Thread-3, started 3356)> name is 杨彦星
data is 5 id(data) is 1763791776
in thread <Thread(Thread-4, started 13728)> name is 杨彦星
data is 5 id(data) is 1763791776
in thread <Thread(Thread-5, started 3712)> name is 杨彦星
data is 5 id(data) is 1763791776

从结果中可以看到,在5个子线程中,data的id都是1763791776,说明在主线程中创建了变量d,在子线程中是可以共享的,在子线程中对共享元素的改变是会影响到其它线程的,所以如果要对共享变量进行修改时,也就是线程不安全的,需要加锁。

自定义类型对象在线程间共享

如果我们要自定义一个类呢,将一个对象作为变量在子线程中传递呢?会是什么效果呢?

#coding:utf-8
import threading
class Data:
  def __init__(self,data=None):
    self.data = data
  def get(self):
    return self.data
  def set(self,data):
    self.data = data
def test(name,data):
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data.get(),id(data)))
if __name__ == '__main__':
  d = Data(10)
  name = "杨彦星"
  print("in main thread id(data) is {}".format(id(d)))
  for i in range(5):
    th = threading.Thread(target=test,args=(name,d))
    th.start()

这里我定义一个简单的类,在主线程初始化了一个该类型的对象d,然后将它作为参数传给子线程,主线程和子线程分别打印了这个对象的id,我们来看一下结果

in main thread id(data) is 2849240813864
in thread <Thread(Thread-1, started 11648)> name is 杨彦星
data is 10 id(data) is 2849240813864
in thread <Thread(Thread-2, started 11016)> name is 杨彦星
data is 10 id(data) is 2849240813864
in thread <Thread(Thread-3, started 10416)> name is 杨彦星
data is 10 id(data) is 2849240813864
in thread <Thread(Thread-4, started 8668)> name is 杨彦星
data is 10 id(data) is 2849240813864
in thread <Thread(Thread-5, started 4420)> name is 杨彦星
data is 10 id(data) is 2849240813864

我们看到,在主线程和子线程中,这个对象的id是一样的,说明它们用的是同一个对象。

无论是标准数据类型还是复杂的自定义数据类型,它们在多线程之间是共享同一个的,但是在多进程中是这样的吗?

多进程之间的共享数据

标准数据类型在进程间共享

还是上面的代码,我们先来看一下int类型的变量的子进程间的共享

#coding:utf-8
import threading
import multiprocessing
def test(name,data):
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data,id(data)))
if __name__ == '__main__':
  d = 10
  name = "杨彦星"
  print("in main thread id(data) is {}".format(id(d)))
  for i in range(5):
    pro = multiprocessing.Process(target=test,args=(name,d))
    pro.start()

得到的结果是

in main thread id(data) is 1763791936
in thread <_MainThread(MainThread, started 9364)> name is 杨彦星
data is 10 id(data) is 1763791936
in thread <_MainThread(MainThread, started 9464)> name is 杨彦星
data is 10 id(data) is 1763791936
in thread <_MainThread(MainThread, started 3964)> name is 杨彦星
data is 10 id(data) is 1763791936
in thread <_MainThread(MainThread, started 10480)> name is 杨彦星
data is 10 id(data) is 1763791936
in thread <_MainThread(MainThread, started 13608)> name is 杨彦星
data is 10 id(data) is 1763791936

可以看到它们的id是一样的,说明用的是同一个变量,但是当我尝试把d由int变为了string时,发现它们又不一样了……

if __name__ == '__main__':
  d = 'yangyanxing'
  name = "杨彦星"
  print("in main thread id(data) is {}".format(id(d)))
  for i in range(5):
    pro = multiprocessing.Process(target=test,args=(name,d))
    pro.start()

此时得到的结果是

in main thread id(data) is 2629633397040
in thread <_MainThread(MainThread, started 9848)> name is 杨彦星
data is yangyanxing id(data) is 1390942032880
in thread <_MainThread(MainThread, started 988)> name is 杨彦星
data is yangyanxing id(data) is 2198251377648
in thread <_MainThread(MainThread, started 3728)> name is 杨彦星
data is yangyanxing id(data) is 2708672287728
in thread <_MainThread(MainThread, started 5288)> name is 杨彦星
data is yangyanxing id(data) is 2376058999792
in thread <_MainThread(MainThread, started 12508)> name is 杨彦星
data is yangyanxing id(data) is 2261044040688

于是我又尝试了list、Tuple、dict,结果它们都是不一样的,我又回过头来试着在多线程中使用列表元组和字典,结果它们还是一样的。

这里有一个有趣的问题,如果是int类型,当值小于等于256时,它们在多进程间的id是相同的,如果大于256,则它们的id就会不同了,这个我没有查看原因。

自定义类型对象在进程间共享

#coding:utf-8
import threading
import multiprocessing
class Data:
  def __init__(self,data=None):
    self.data = data
  def get(self):
    return self.data
  def set(self,data):
    self.data = data
def test(name,data):
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data.get(),id(data)))

if __name__ == '__main__':
  d = Data(10)
  name = "杨彦星"
  print("in main thread id(data) is {}".format(id(d)))
  for i in range(5):
    pro = multiprocessing.Process(target=test,args=(name,d))
    pro.start()

得到的结果是

in main thread id(data) is 1927286591728
in thread <_MainThread(MainThread, started 2408)> name is 杨彦星
data is 10 id(data) is 1561177927752
in thread <_MainThread(MainThread, started 5728)> name is 杨彦星
data is 10 id(data) is 2235260514376
in thread <_MainThread(MainThread, started 1476)> name is 杨彦星
data is 10 id(data) is 2350586073040
in thread <_MainThread(MainThread, started 996)> name is 杨彦星
data is 10 id(data) is 2125002248088
in thread <_MainThread(MainThread, started 10740)> name is 杨彦星
data is 10 id(data) is 1512231669656

可以看到它们的id是不同的,也就是不同的对象。

在多进程间如何共享数据

我们看到,数据在多进程间是不共享的(小于256的int类型除外),但是我们又想在主进程和子进程间共享一个数据对象时该如何操作呢?

在看这个问题之前,我们先将之前的多线程代码做下修改

#coding:utf-8
import threading
import multiprocessing
class Data:
  def __init__(self,data=None):
    self.data = data
  def get(self):
    return self.data
  def set(self,data):
    self.data = data
def test(name,data,lock):
  lock.acquire()
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data,id(data)))
  data.set(data.get()+1)
  lock.release()
if __name__ == '__main__':
  d = Data(0)
  thlist = []
  name = "yang"
  lock = threading.Lock()
  for i in range(5):
    th = threading.Thread(target=test,args=(name,d,lock))
    th.start()
    thlist.append(th)
  for i in thlist:
    i.join()
  print(d.get())

我们这个代码的目的是这样,使用自定义的Data类型对象,当经过5个子线程操作以后,每个子线程对其data值进行加1操作,最后在主线程打印对象的data值。

该输出结果如下

in thread <Thread(Thread-1, started 3296)> name is yang
data is <__main__.Data object at 0x000001A451139198> id(data) is 1805246501272
in thread <Thread(Thread-2, started 9436)> name is yang
data is <__main__.Data object at 0x000001A451139198> id(data) is 1805246501272
in thread <Thread(Thread-3, started 760)> name is yang
data is <__main__.Data object at 0x000001A451139198> id(data) is 1805246501272
in thread <Thread(Thread-4, started 1952)> name is yang
data is <__main__.Data object at 0x000001A451139198> id(data) is 1805246501272
in thread <Thread(Thread-5, started 5988)> name is yang
data is <__main__.Data object at 0x000001A451139198> id(data) is 1805246501272

可以看到在主线程最后打印出来了5,符合我们的预期,但是如果放到多进程中呢?因为多进程下,每个子进程所持有的对象是不同的,所以每个子进程操作的是各自的Data对象,对于主进程的Data对象应该是没有影响的,我们来看下它的结果

#coding:utf-8
import threading
import multiprocessing
class Data:
  def __init__(self,data=None):
    self.data = data
  def get(self):
    return self.data
  def set(self,data):
    self.data = data
def test(name,data,lock):
  lock.acquire()
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data,id(data)))
  data.set(data.get()+1)
  lock.release()
if __name__ == '__main__':
  d = Data(0)
  thlist = []
  name = "yang"
  lock = multiprocessing.Lock()
  for i in range(5):
    th = multiprocessing.Process(target=test,args=(name,d,lock))
    th.start()
    thlist.append(th)
  for i in thlist:
    i.join()
  print(d.get())

它的输出结果是:

in thread <_MainThread(MainThread, started 7604)> name is yang
data is <__mp_main__.Data object at 0x000001D110130EB8> id(data) is 1997429477048
in thread <_MainThread(MainThread, started 12108)> name is yang
data is <__mp_main__.Data object at 0x000002C4E88E0E80> id(data) is 3044738469504
in thread <_MainThread(MainThread, started 3848)> name is yang
data is <__mp_main__.Data object at 0x0000027827270EF0> id(data) is 2715076202224
in thread <_MainThread(MainThread, started 12368)> name is yang
data is <__mp_main__.Data object at 0x000002420EA80E80> id(data) is 2482736991872
in thread <_MainThread(MainThread, started 4152)> name is yang
data is <__mp_main__.Data object at 0x000001B1577F0E80> id(data) is 1861188783744

最后的输出是0,说明了子进程对于主进程传入的Data对象操作其实对于主进程的对象是不起作用的,我们需要怎样的操作才能实现子进程可以操作主进程的对象呢?我们可以使用 multiprocessing.managers 下的 BaseManager 来实现

#coding:utf-8
import threading
import multiprocessing
from multiprocessing.managers import BaseManager
class Data:
  def __init__(self,data=None):
    self.data = data
  def get(self):
    return self.data
  def set(self,data):
    self.data = data
BaseManager.register("mydata",Data)
def test(name,data,lock):
  lock.acquire()
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data,id(data)))
  data.set(data.get()+1)
  lock.release()
def getManager():
  m = BaseManager()
  m.start()
  return m
if __name__ == '__main__':
  manager = getManager()
  d = manager.mydata(0)
  thlist = []
  name = "yang"
  lock = multiprocessing.Lock()
  for i in range(5):
    th = multiprocessing.Process(target=test,args=(name,d,lock))
    th.start()
    thlist.append(th)
  for i in thlist:
    i.join()
  print(d.get())

使用 from multiprocessing.managers import BaseManager 引入 BaseManager以后,在定义完Data类型之后,使用 BaseManager.register("mydata",Data) 将Data类型注册到BaseManager中,并且给了它一个名字叫 mydata ,之后就可以使用 BaseManager 对象的这个名字来初始化对象,我们来看一下输出

C:\Python35\python.exe F:/python/python3Test/multask.py
in thread <_MainThread(MainThread, started 12244)> name is yang
data is <__mp_main__.Data object at 0x000001FE1B7D9668> id(data) is 2222932504080
in thread <_MainThread(MainThread, started 2860)> name is yang
data is <__mp_main__.Data object at 0x000001FE1B7D9668> id(data) is 1897574510096
in thread <_MainThread(MainThread, started 2748)> name is yang
data is <__mp_main__.Data object at 0x000001FE1B7D9668> id(data) is 2053415775760
in thread <_MainThread(MainThread, started 7812)> name is yang
data is <__mp_main__.Data object at 0x000001FE1B7D9668> id(data) is 2766155820560
in thread <_MainThread(MainThread, started 2384)> name is yang
data is <__mp_main__.Data object at 0x000001FE1B7D9668> id(data) is 2501159890448

我们看到,虽然在每个子进程中使用的是不同的对象,但是它们的值却是可以“共享”的。

标准的数据类型也可以通过multiprocessing库中的Value对象,举一个简单的例子

#coding:utf-8
import threading
import multiprocessing
from multiprocessing.managers import BaseManager
class Data:
  def __init__(self,data=None):
    self.data = data
  def get(self):
    return self.data
  def set(self,data):
    self.data = data
BaseManager.register("mydata",Data)
def test(name,data,lock):
  lock.acquire()
  print("in thread {} name is {}".format(threading.current_thread(),name))
  print("data is {} id(data) is {}".format(data,id(data)))
  data.value +=1
  lock.release()
if __name__ == '__main__':
  d = multiprocessing.Value("l",10) #
  print(d)
  thlist = []
  name = "yang"
  lock = multiprocessing.Lock()
  for i in range(5):
    th = multiprocessing.Process(target=test,args=(name,d,lock))
    th.start()
    thlist.append(th)
  for i in thlist:
    i.join()
  print(d.value)

这里使用 d = multiprocessing.Value("l",10) 初始化了一个数字类型的对象,这个类型是 Synchronized wrapper for c_long , multiprocessing.Value 在初始化时,第一个参数是类型,第二个参数是值,具体支持的类型如下

还可以使用ctypes库里和类初始化字符串

>>> from ctypes import c_char_p
>>> s = multiprocessing.Value(c_char_p, b'\xd1\xee\xd1\xe5\xd0\xc7')
>>> print(s.value.decode('gbk'))

杨彦星

还可以使用Manager对象初始list,dict等

#coding:utf-8
import multiprocessing
def func(mydict, mylist):
  # 子进程改变dict,主进程跟着改变
  mydict["index1"] = "aaaaaa" 
  # 子进程改变List,主进程跟着改变 
  mydict["index2"] = "bbbbbb"
  mylist.append(11) 
  mylist.append(22)
  mylist.append(33)
if __name__ == "__main__":
  # 主进程与子进程共享这个字典
  mydict = multiprocessing.Manager().dict()
  # 主进程与子进程共享这个List
  mylist = multiprocessing.Manager().list(range(5)) 
  p = multiprocessing.Process(target=func, args=(mydict, mylist))
  p.start()
  p.join()
  print(mylist)
  print(mydict)

其实我们这里所说的共享只是数据值上的共享,因为在多进程中,各自持有的对象都不相同,所以如果想要同步状态需要曲线救国。不过这种在自己写的小项目中可以简单的使用,如果做一些大一点的项目,还是建议不要使用这种共享数据的方式,这种大大的增加了程序间的耦合性,使用逻辑变得复杂难懂,所以建议还是使用队列或者数据为进行间通信的渠道。

总结

以上所述是小编给大家介绍的处理python中多线程与多进程中的数据共享问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
python连接mysql调用存储过程示例
Mar 05 Python
全面解读Python Web开发框架Django
Jun 30 Python
python将MongoDB里的ObjectId转换为时间戳的方法
Mar 13 Python
python通过floor函数舍弃小数位的方法
Mar 17 Python
python判断字符串是否包含子字符串的方法
Mar 24 Python
python多线程方式执行多个bat代码
Jun 07 Python
python 爬虫出现403禁止访问错误详解
Mar 11 Python
python 显示数组全部元素的方法
Apr 19 Python
python 顺时针打印矩阵的超简洁代码
Nov 14 Python
python 三元运算符使用解析
Sep 16 Python
python实现ip地址的包含关系判断
Feb 07 Python
Python-jenkins模块之folder相关操作介绍
May 12 Python
在django中图片上传的格式校验及大小方法
Jul 28 #Python
python之生产者消费者模型实现详解
Jul 27 #Python
python单线程下实现多个socket并发过程详解
Jul 27 #Python
如何使用python操作vmware
Jul 27 #Python
利用python计算windows全盘文件md5值的脚本
Jul 27 #Python
Djang的model创建的字段和参数详解
Jul 27 #Python
python中bs4.BeautifulSoup的基本用法
Jul 27 #Python
You might like
php&amp;java(二)
2006/10/09 PHP
纯php打造的tab选项卡效果代码(不用js)
2010/12/29 PHP
input file获得文件根目录简单实现
2013/04/26 PHP
php中ltrim()、rtrim()与trim()删除字符空格实例
2014/11/25 PHP
PHP解压tar.gz格式文件的方法
2016/02/14 PHP
Joomla数据库操作之JFactory::getDBO用法
2016/05/05 PHP
老生常谈php中传统验证与thinkphp框架(必看篇)
2017/06/10 PHP
laravel + vue实现的数据统计绘图(今天、7天、30天数据)
2018/07/31 PHP
PHP的图像处理实例小结【文字水印、图片水印、压缩图像等】
2019/12/20 PHP
PHP生成图表pChart的示例解析
2020/07/31 PHP
用js实现的模拟jquery的animate自定义动画(2.5K)
2010/07/20 Javascript
Kindeditor在线文本编辑器如何过滤HTML
2016/04/14 Javascript
Vuejs入门教程之Vue生命周期,数据,手动挂载,指令,过滤器
2017/04/19 Javascript
你有必要知道的10个JavaScript难点
2017/07/25 Javascript
js字符串倒序的实例代码
2018/11/30 Javascript
koa+jwt实现token验证与刷新功能
2019/05/30 Javascript
微信小程序错误this.setData报错及解决过程
2019/09/18 Javascript
JavaScript中如何对多维数组(矩阵)去重的实现
2019/12/04 Javascript
[28:28]Ti4 冒泡赛第二天NEWBEE vs NaVi 2
2014/07/15 DOTA
利用Python查看目录中的文件示例详解
2017/08/28 Python
Python基于递归和非递归算法求两个数最大公约数、最小公倍数示例
2018/05/21 Python
解决pycharm 远程调试 上传 helpers 卡住的问题
2019/06/27 Python
python爬虫 爬取58同城上所有城市的租房信息详解
2019/07/30 Python
Python 列表的清空方式
2020/01/13 Python
Python建造者模式案例运行原理解析
2020/06/29 Python
css3中的calc函数浅析
2018/07/10 HTML / CSS
纯CSS3实现运行时钟的示例代码
2021/01/25 HTML / CSS
浅析canvas元素的html尺寸和css尺寸对元素视觉的影响
2019/07/22 HTML / CSS
铭宣海淘转运:美国、日本、英国转运等全球转运公司
2019/09/10 全球购物
师范学院美术系毕业生自我鉴定
2014/01/29 职场文书
中班下学期个人工作总结
2015/02/12 职场文书
超市员工管理制度
2015/08/06 职场文书
高中体育课教学反思
2016/02/16 职场文书
导游词之新疆尼雅遗址
2019/10/16 职场文书
一行代码python实现文件共享服务器
2021/04/22 Python
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
2021/05/18 Vue.js