一、引入
展望未来,基于消息传递的并发编程是大势所趋;即便是使用线程,推荐做法也是将程序设计为大量独立的线程集合。通过消息队列交换数据。这样极大地减少了对使用锁定和其他同步手段的需求,还可以扩展到分布式系统中。
进程间应该尽量避免通信,即便需要通信,也应该选择进程安全的工具来避免加锁带来的问题,应该尽量避免使用本节所讲的共享数据的方式,以后我们会尝试使用数据库来解决进程之间的数据共享问题。
二、Manager模块 - 实现数据共享
1、定义
进程间数据是独立的,可以借助于队列或管道实现通信,二者都是基于消息传递的;虽然进程间数据独立,但可以通过Manager实现数据共享,事实上Manager的功能远不止于此。
多进程共同去处理共享数据的时候,就和我们多进程同时去操作一个文件中的数据是一样的,不加锁就会出现错误的结果,进程不安全的,所以也需要加锁
1 A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies.
2
3 A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.
1 from multiprocessing import Process,Manager
2
3 def func(m_dic):
4 m_dic['name'] = 'alex_luffy' # 子进程对数据进行了修改
5
6 if __name__ == '__main__':
7 m = Manager()
8 m_dic = m.dict({'name':'alex'})
9 print('主进程', m_dic) # 先记录一下原始数据是啥样的
10 p = Process(target=func,args=(m_dic,)) # 将字典数据传递给子进程
11 p.start()
12 p.join() # 先让子进程执行完毕
13
14 print('主进程',m_dic)
15 结果:
16 主进程 {'name': 'alex'}
17 主进程 {'name': 'alex_luffy'} - m_dic内的数据被子进程修改了
2、创建多进程取修改子进程的数据(会出现数据不安全->会导致数据混乱加锁解决)
解决数据不安全
1 # 设置一个100数字,创建20个进程,分别去减一
2
3 from multiprocessing import Process,Manager,Lock
4
5 def func(m_dic,ml):
6 #不加锁的情况会出现数据错乱
7 # m_dic['count'] -= 1
8
9 #加锁,这是另外一种加锁形式
10 with ml:
11 m_dic['count'] -= 1
12
13 #等同
14 # ml.acquire()
15 # m_dic['count'] -= 1
16 # ml.release()
17
18 if __name__ == '__main__':
19 m = Manager()
20 ml = Lock()
21 m_dic = m.dict({'count':100})
22 # print('主进程', m_dic)
23 p_list = []
24
25 #开启20个进程来对共享数据进行修改
26 for i in range(20):
27 p1 = Process(target=func,args=(m_dic,ml,))
28 p1.start()
29 p_list.append(p1)
30 [ppp.join() for ppp in p_list]
31
32 print('主进程',m_dic)
3、总结
下面要讲的信号量和事件也相当于锁,也是全局的,所有进程都能拿到这些锁的状态,进程之间这些锁啊信号量啊事件啊等等的通信,其实底层还是socekt,只不过是基于文件的socket通信,而不是跟上面的数据共享啊空间共享啊之类的机制,我们之前学的是基于网络的socket通信,还记得socket的两个家族吗,一个文件的一个网络的,所以将来如果说这些锁之类的报错,可能你看到的就是类似于socket的错误,简单知道一下就可以啦~~~