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

多线程之间的共享数据

Python资源共享群:484031800

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

看以下代码

#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 = 5name = "杨彦星"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 = datadef get(self):return self.datadef 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 = 10name = "杨彦星"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 = datadef get(self):return self.datadef 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 = datadef get(self):return self.datadef 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

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

#coding:utf-8
import threading
import multiprocessing
class Data:def __init__(self,data=None):self.data = datadef get(self):return self.datadef 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

最后的输出是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 = datadef get(self):return self.datadef set(self,data):self.data = dataBaseManager.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
5

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

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

#coding:utf-8
import threading
import multiprocessing
from multiprocessing.managers import BaseManager
class Data:def __init__(self,data=None):self.data = datadef get(self):return self.datadef 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 +=1lock.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()# 主进程与子进程共享这个Listmylist = multiprocessing.Manager().list(range(5))  p = multiprocessing.Process(target=func, args=(mydict, mylist))p.start()p.join()print(mylist)print(mydict)

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

---------------------
作者:pythoncxy
来源:CSDN
原文:https://blog.csdn.net/Pythoncxy/article/details/97641057
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

[转]Python中多线程与多进程中的数据共享问题!相关推荐

  1. python 多线程 廖雪峰_python中多线程与多进程中的数据共享问题

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

  2. python的多线程和多进程网络编程

    二十八.python的多线程和多进程网络编程 线程和进程关系: 进程是具有独立功能的程序,进程是系统进行资源分配和调度的一个独立单位 线程是进程的一个实体,是cpu调度的基本单位,它是比进程更小的能独 ...

  3. io密集型和cpu密集型_一次说明白Python爬虫中多线程,多进程,异步IO编程

    图/文:迷神 我们在Python爬虫中,重要的是讲究速度,如果有10万或者100万Url地址,写过爬虫的都会知道,那估计是非常慢的.我们的Python爬虫一般IO密集型业务,Python爬虫程序需要发 ...

  4. 关于python的多线程和多进程_Python的多线程和多进程

    (1)多线程的产生并不是因为发明了多核CPU甚至现在有多个CPU+多核的硬件,也不是因为多线程CPU运行效率比单线程高.单从CPU的运行效率上考虑,单任务进程及单线程效率是最高的,因为CPU没有任何进 ...

  5. Python的多线程、多进程及协程

    Python 代码执行由python虚拟机控制,每个CPU在任意时刻只有一个线程在解释器运行,对python虚拟机的访问由全局解释锁GIL控制,如在单核CPU时多线程是并发不是并行. 并发:两个或多个 ...

  6. 爬虫-python -(8) 多线程与多进程操作以及线程池 异步操作

    文章目录 1.通过异步操作提高爬虫效率 2.多线程 3.多进程 4.线程池与进程池 5.线程池实例-新发地菜价保存 6.总结 1.通过异步操作提高爬虫效率 一般爬虫过程为,请求网页-响应请求-从响应中 ...

  7. python多线程和多进程的区别_python中多线程与多进程的区别

    线程的概念: 线程是操作系统中进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程可以有多个线程,每条线程可以同时执行不同的任务.一个 ...

  8. python的多线程与多进程

    1.GIL(Global Interpreter Lock) 在python中,由于GIL(Global Interpreter Lock)全局解释器锁的存在,所以多线程并不是真正意义上的多线程.在 ...

  9. python+selenium多线程与多进程爬虫

    使用python+selenium抓取深圳证券交易所本所公告数据,刚开始是用单进程爬取的,最近将代码修改了一下,分别用多进程和多线程进行抓取,速度非常快.如果对selenium不了解的请移步别的地方学 ...

最新文章

  1. 工信部:2017工业物联网白皮书
  2. Kotlin学习 PART 2:kotlin基础
  3. 【下载】《看见新力量》第二期,带你走进数十位科技创业者背后的故事
  4. php 获取cookieid,Redis实现Session共享详解
  5. SQL Server 之 在与SQLServer建立连接时出现与网络相关的或特定于实例的错误
  6. 2.4 shell 脚本基础
  7. MyEclipse的快捷使用(含关联源码和Doc的方式)
  8. 苹果Mac 3D 建模渲染软件:Vectorworks
  9. lg android平台驱动程序,lg g3刷KDZ教程-KDZ线刷工具及USB驱动下载
  10. 高速收费站简笔画_桥的简笔画有哪些
  11. 图解三层交换机:局域网都用它来组网
  12. Delphi中使用ReportMachine 6.5中汇总行不进行汇总的设置问题
  13. FullScreen实现全屏和分屏
  14. 高通功耗调试17之TLOC DEAMON导致待机/亮屏电流异常问题
  15. 网页只有在服务器上才能打开,为什么你的电脑只能上QQ,而打不开网页?我来告诉你...
  16. 从Ajax聊一聊Jsonp hijacking
  17. html5 图片羽化,课题:html5图像羽化(不规则区域羽化,feather,html5羽化)
  18. [UOJ#454][UER#8]打雪仗
  19. 懒羊羊找朋友 C++
  20. android 适配7.0,Android7.0适配心得(一)_拍照兼容

热门文章

  1. I have to mention the search function at the
  2. 第四次作业:猫狗大战挑战赛
  3. 移除加密的pdf文件密码
  4. SQL截断增强功能:SQL Server 2019中的静默数据截断
  5. kuberneters dashboard认证及分级授权
  6. Matlab小波工具箱的使用2
  7. [翻译 EF Core in Action 1.7] MyFirstEfCoreApp访问的数据库
  8. 深入解析Koa之核心原理
  9. [学习笔记]舞蹈链(Dancing Links)C++实现(指针版)
  10. 微信开发学习日记(八):7步看懂weiphp插件机制,核心目标是响应微信请求