8-2-4python语法基础-并发编程-进程和线程对比学习:进程通信和线程通信
前言
进程和线程,有很多地方非常类似,包括使用的方法也很多相同的,
所以我决定放到一起对比学习,
这一篇,专门对比:
进程通信
线程通信
进程间通信
多线程:共享变量很简单,直接定义全局 global 变量即可
多进程:全局变量在多个进程中不共享,进程之间的数据是独立的,默认情况下互不影响。
- 进程之间的数据隔离问题,也就是不能共享全局变量
- 进程和进程之间的数据肯定是隔离的,比如qq和微信,不隔离就坏事了
- 几个进程之间,如果不通过特殊的手段,是不可能共享一个数据的,这个记住,没有什么可理解的,
from multiprocessing import Process
num=1
def work1():
global num
num+=5
print('子进程1运行,num:',num)
def work2():
global num
num += 10
print('子进程2运行,num:',num)
if __name__=='__main__':
print('父进程开始运行')
p1=Process(target=work1)
p2=Process(target=work2)
p1.start()
p2.start()
p1.join()
p2.join()
执行结果
父进程开始运行
子进程1运行,num:6
子进程2运行,num:11
进程之间通信
场景:安卓ui自动化框架,使用的是多进程实现的多设备并行。而在捞取数据做数据汇总时,需要多进程可以数据共享。
进程之间通信1,---管道
- 上面我们看到了,进程之间是不能通信的,那怎么解决进程之间的通信问题呢,可以使用管道,
from multiprocessing import Pipe
进程之间通信2,---队列
- 认知队列的操作
- 1,主要是put和get,
- 2,使用这个队列,可以实现复杂的生产者和消费者模型,这个将是主要的进程之间通信的方式,
from multiprocessing import Process, Queue, set_start_method
import time, random, os
def consumer(q):
while True:
res = q.get()
if res is None:
break # 收到结束信号则结束
time.sleep(random.randint(1, 3))
print('\033[45m%s 吃 %s\033[0m' % (os.getpid(), res))
def producer(q):
for i in range(10):
time.sleep(random.randint(1, 3))
res = '包子%s' % i
q.put(res)
print('\033[46m%s 生产了 %s\033[0m' % (os.getpid(), res))
q.put(None) # 发送结束信号
if __name__ == '__main__':
set_start_method('fork')
q = Queue()
# 生产者们:即厨师们
p1 = Process(target=producer, args=(q,))
# 消费者们:即吃货们
c1 = Process(target=consumer, args=(q,))
# 开始
p1.start()
c1.start()
print('进程间通信-队列-主进程')
进程之间通信3,---数据共享
共享数值型数据
主进程与子进程共用一个value
import multiprocessing
def func(num):
num.value = 2
if __name__ == "__main__":
num = multiprocessing.Value("d", 1)
p = multiprocessing.Process(target=func, args=(num,))
p.start()
p.join()
共享数组型数据: 共享list实践
子进程改变dict,list,主进程dict,list跟着改变
进程间通讯有多种方式,包括信号,管道,消息队列,信号量,共享内存,socket等。这里主要介绍使用multiprocessing.Manager模块实现进程间共享数据。
Manager() 可以创建一个专门用来维护进程间数据共享的进程,它能提供 python 所支持的任何数据结构,也能用来实现进程间的数据通信。
Manager支持的类型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array。
现在想把每个手机的自动化运行结果的元组拿出来分析,使用共享list的方式
多进程中,同一个变量,各自有一份拷贝存在于每个进程中, 互不影响;
多线程中,所有变量都由所有线程共享,任何一个变量都可以被任何一个线程修改.
import time
from multiprocessing import Process, Manager
def producer(m_list):
print('producer producing goods start')
for i in range(1, 6):
m_list.append(f'good_{i}')
print('producer producing goods end')
def consumer(m_list):
while m_list:
goods_name = m_list.pop()
print(f'consumer consuming goods: {goods_name} start')
time.sleep(1)
print(f'consumer consuming goods: {goods_name} end')
if __name__ == "__main__":
print('----主进程开始----')
m_list = Manager().list()
producer_process = Process(target=producer, args=(m_list,))
consumer_process = Process(target=consumer, args=(m_list,))
producer_process.start()
consumer_process.start()
producer_process.join()
consumer_process.join()
print('----主进程结束----')
这种生产者-消费者模型,使用pop的方式,不会设计到操作同一个数据的问题,
但是要知道如果使用这种数据共享,设计到操作同一个数据的问题,
要注意进程冲突的问题,很可能导致数据被错误修改,出现数据不一致的情况。
所以多个进程如果同时操作共享内存中的同一个资源时,需要加锁,且这里加的锁必须是 Manager 共享对象中的锁。
案例:
# coding=utf-8
from multiprocessing import Manager,Process
def task1(num, lock):
for i in range(10000):
with lock:
num.value += 1
def task2(num, lock):
for i in range(10000):
with lock:
num.value -= 1
if __name__ == "__main__":
manager = Manager() # 定义一个共享内存对象
num = manager.Value("jaye", 7)
lock = manager.Lock() # 获取锁
p1 = Process(target=task1, args=(num, lock))
p2 = Process(target=task2, args=(num, lock))
p1.start()
p2.start()
p1.join()
p2.join()
print(f"主进程的num:{num.value}") # 7
总结
- 进程间的三种通信(IPC)方式:
- 方式一:队列(推荐使用)
- 方式二:管道(不推荐使用,了解即可)
- 管道相当于队列,但是管道不自动加锁
- 方式三:共享数据(不推荐使用,了解即可)
- 共享数据也没有自动加锁的功能,所以还是推荐用队列的。感兴趣的可以研究研究管道和共享数据
记住进程之间,因为数据不同享,所以会有进程之间通信的问题,
但是线程不一样,线程之间可以共享全局变量,所以只要通过全局变量就可以实现线程的通信,
这一点要记住,