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

多线程之间的共享数据

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

看以下代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14#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,得到了如下的结果

1

2

3

4

5

6

7

8

9

10in thread name is 杨彦星

data is 5 id(data) is 1763791776

in thread name is 杨彦星

data is 5 id(data) is 1763791776

in thread name is 杨彦星

data is 5 id(data) is 1763791776

in thread name is 杨彦星

data is 5 id(data) is 1763791776

in thread name is 杨彦星

data is 5 id(data) is 1763791776

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

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25#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,我们来看一下结果

1

2

3

4

5

6

7

8

9

10

11in main thread id(data) is 2849240813864

in thread name is 杨彦星

data is 10 id(data) is 2849240813864

in thread name is 杨彦星

data is 10 id(data) is 2849240813864

in thread name is 杨彦星

data is 10 id(data) is 2849240813864

in thread name is 杨彦星

data is 10 id(data) is 2849240813864

in thread name is 杨彦星

data is 10 id(data) is 2849240813864

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

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

多进程之间的共享数据

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16#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()

得到的结果是

1

2

3

4

5

6

7

8

9

10

11in main thread id(data) is 1763791936

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1763791936

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1763791936

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1763791936

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1763791936

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1763791936

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

1

2

3

4

5

6

7if __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()

此时得到的结果是

1

2

3

4

5

6

7

8

9

10

11in main thread id(data) is 2629633397040

in thread <_mainthread started> name is 杨彦星

data is yangyanxing id(data) is 1390942032880

in thread <_mainthread started> name is 杨彦星

data is yangyanxing id(data) is 2198251377648

in thread <_mainthread started> name is 杨彦星

data is yangyanxing id(data) is 2708672287728

in thread <_mainthread started> name is 杨彦星

data is yangyanxing id(data) is 2376058999792

in thread <_mainthread started> name is 杨彦星

data is yangyanxing id(data) is 2261044040688

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

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

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26#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()

得到的结果是

1

2

3

4

5

6

7

8

9

10

11in main thread id(data) is 1927286591728

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1561177927752

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 2235260514376

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 2350586073040

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 2125002248088

in thread <_mainthread started> name is 杨彦星

data is 10 id(data) is 1512231669656

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

在多进程间如何共享数据

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34#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值。

该输出结果如下

1

2

3

4

5

6

7

8

9

10

11in thread name is yang

data is <__main__.data object at> id(data) is 1805246501272

in thread name is yang

data is <__main__.data object at> id(data) is 1805246501272

in thread name is yang

data is <__main__.data object at> id(data) is 1805246501272

in thread name is yang

data is <__main__.data object at> id(data) is 1805246501272

in thread name is yang

data is <__main__.data object at> id(data) is 1805246501272

5

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34#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())

它的输出结果是:

1

2

3

4

5

6

7

8

9

10

11in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 1997429477048

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 3044738469504

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 2715076202224

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 2482736991872

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 1861188783744

0

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45#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对象的这个名字来初始化对象,我们来看一下输出

1

2

3

4

5

6

7

8

9

10

11

12C:\Python35\python.exe F:/python/python3Test/multask.py

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 2222932504080

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 1897574510096

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 2053415775760

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 2766155820560

in thread <_mainthread started> name is yang

data is <__mp_main__.data object at> id(data) is 2501159890448

5

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

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38#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库里和类初始化字符串

1

2

3

4>>>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等

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26#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中多线程与多进程中的数据共享问题相关推荐

  1. python 多线程 廖雪峰_Python 一篇学会多线程

    多线程和多进程是什么自行google补脑,廖雪峰官网也有,但是不够简洁,有点晕,所以就整个简单的范例. 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用 ...

  2. 《python(廖雪峰课程)》学习笔记

    <python(廖雪峰课程)>学习笔记(个人检索) 1. 第一个python程序 input & output print输出字符串可以用""or'' prin ...

  3. 02-Python解释器-Python笔记-廖雪峰

    02-Python解释器-Python笔记-廖雪峰 CPython 官方版本的解释器:CPython.这个解释器是用C语言开发的,所以叫CPython. IPython IPython是基于CPyth ...

  4. python 廖雪峰_python学习(廖雪峰的官方网站)

    廖雪峰的官方网站. 请注意区分命令行模式和Python交互模式. 在命令行模式下,可以直接运行.py文件. 2运行其实和java运行一样 cd dir 3 print() input() 4数据类型 ...

  5. 【python】廖雪峰学习笔记

    廖雪峰python学习笔记 Python解释器 python 基础 输入输出 数据类型和变量 字符串和编码 list和tuple dict和set del删除的是变量,而不是数据 函数 高级特性 函数 ...

  6. python教程廖雪峰云-Python教程

    [TOC] ### 教程简介 这是小白的Python新手教程,具有如下特点: **中文,免费,零起点,完整示例,基于最新的Python 3版本.** Python是一种计算机程序设计语言.你可能已经听 ...

  7. python教程 廖雪_Python 2.7教程

    这是小白的Python新手教程. Python是一种计算机程序设计语言.你可能已经听说过很多种流行的编程语言,比如非常难学的C语言,非常流行的Java语言,适合初学者的Basic语言,适合网页编程的J ...

  8. 廖雪峰python视频教程-廖雪峰Python教程的配套视频教程,全套完整版!

    事实上,我很久以前就用Python编写了一个简单的爬虫程序.爬网站数据或图片真的很有趣.但我并没有系统地研究过Python,所以我总是有点痴迷. 另外,从我个人的感觉来看,人类无法阻止Python的发 ...

  9. 开课吧python怎么样-廖雪峰官方大数据分析课程,第七期对外开放,前20名直降2000元!!!...

    数据分析是程序员或者机器学习从业者最基本的一项技能,对数据进行掌握了解可以迅速抓住业务主线和数据特征! 关于数据分析的学习资料网上一抓一大把,但是一提项目实践,很多人表示手上没有数据源,不知道完整的一 ...

最新文章

  1. 时间序列的自回归理论和实现
  2. (007) java后台开发之Scanner报错java.util.NoSuchElementException
  3. TensorFlow实现案例汇集:代码+笔记
  4. 第四范式入选Forrester中国机器学习Now Tech™,成唯一AutoML专注类大型厂商
  5. leetcode 122. 买卖股票的最佳时机 II 思考分析
  6. ai二维码插件_送你60款AI脚本插件包,已整合成插件面板的形式,方便在AI中调用...
  7. django跳转到其他页面的方式
  8. efuse 加密文件 linux,乐鑫关于“故障注入”和 “eFuse 保护”的安全建议 (CVE-2019-17391)...
  9. 【OpenGL】Shader实例分析(七)- 雪花飘落效果
  10. AMPL-段阶段生产模型
  11. 储户诉银行虚假宣传 微众银行智能存款产品屡遭用户投诉
  12. 编码格式检测(中文乱码问题)
  13. 交互媒体专题设计大作业
  14. js 控制字符串长度 区分中英文
  15. 如何解决 win10 2016Excel 显示界面全为英文的情况
  16. 力扣(441.26)补8.24
  17. PCL——超体素(SuperVoxel)、超体聚类分割
  18. thinksns java_社交系统ThinkSNS+技术概要
  19. MySQL存储引擎 lnnoDB逻辑架构 innodb存储引擎表空间(ibd文件)详解 回滚日志的物理空间
  20. 研究生的压力应对与心理健康(雨课堂)研究生 全部答案

热门文章

  1. 怎么去除表中的系统导出的字符_EXCEL非常有用的字符函数LEN、LENB,财务工作者的必备利器...
  2. xs资料网-产品设计图档下载_proe玩具车3D模型图档下载creo4.0汽车模型下载中磊教育...
  3. presto java_Presto Jdbc
  4. 线性代数学的中国根源
  5. 院士拿布袋领奖归来,朋友圈刷屏了
  6. 献给 Python 开发人员的 25 个最佳 GitHub 代码库!
  7. AI如何用特征、权重和避免过拟合等技术来分辨冬瓜和西瓜?
  8. 用面包分析数学定律,数学老师成网红
  9. NS2:undefined reference to `xxx' collect2: error: ld returned 1 exit status
  10. C/C++语言函数学习(4)字符串处理函数