Python进程间通信之管道、消息队列、共享内存

由于进程空间相对独立,资源无法共享,基于这种情况,就需要使用一些方法使得不同的进程之间可以进行通信。

我这里介绍三种进程间通信的方式:管道、消息队列、共享内存。

管道

管道的通信原理:

  • 在内存中开辟管道空间,生成管道操作对象,多个进程使用"同一个"管道对象进行操作即可实现通信

代码实现:

from multiprocessing import Process,Pipe

#创建管道对象
fd1,fd2 = Pipe(duplex = False)

def test(msg):
    #向管道写入内容
    fd2.send(msg)

jobs = []
for i in range(5):
	msg = "No." + str(i)
	p = Process(target = test, args = (msg,))
	jobs.append(p)
	p.start()

for i in range(5):
    #读取管道
    data = fd1.recv()
    print(data)

# 回收进程
for i in jobs:
    i.join()

其中:

fd1, fd2 = Pipe(duplex = True)

  • 功能:创建管道
  • 参数(duplex):默认为True表示双向管道,如果设置为False则为单向管道
  • 返回值:表示管道的两端,如果是双向管道,都可以读写,如果是单向管道,则fd1只读,fd2只写

fd.send(data)

  • 功能:向管道写入内容
  • 参数:要写入的内容(可以发送python数据类型)

fd.recv()

  • 功能:从管道读取内容
  • 返回值:读取到的内容
  • 如果管道为空则阻塞等待

运行结果:

gk@gk-vm:~/python/process_communication$ python3 pipe_test.py 
No.0
No.1
No.3
No.4
No.2

观察读取的内容,会发现读取的顺序是不一定的。我们是有序的将内容放入管道的,但是取出来的数据确实无序的,说明管道的读取不是按照顺序读取的。

消息队列

消息队列的通信原理:

  • 在内存中建立队列(先进先出)数据结构模型。多个进程都可以向队列存入内容,取出内容的顺序和存入顺序保持一致。

代码实现:

from multiprocessing import Process,Queue
import time

#创建消息队列
q = Queue(maxsize=3)

# 向队列中放入数据
def test1():
    time.sleep(3)
    q.put("数据1")
    q.put("数据2")

# 从队列中取出数据
def test2():
    time.sleep(1)
    # block=True,timeout=5表示如果队列为空则阻塞等待五秒,如果五秒后队列中依然为空,就会停止阻塞,抛出queue.Empty异常,比如把这里的timeout改为1秒,就会抛出异常
    print("收到消息:", q.get(block=True, timeout=5))

p1 = Process(target = test1)
p2 = Process(target = test2)
p1.start()
p2.start()
p1.join()
p2.join()

# 队列是否已满
print("q.full():", q.full())
# 队列是否为空
print("q.empty():", q.empty())
# 队列中消息数量
print("q.qsize():", q.qsize())

# 关闭队列
q.close()

# 在队列关闭后尝试获取数据
try:
	print(q.get())
except OSError as e:
	print("消息队列已关闭")
	print("error:", e)

其中:

q = Queue(maxsize = 3)

  • 功能:创建消息队列
  • 参数(maxsize):表示最多存放多少消息。默认是根据内存分配存储
  • 返回值:队列对象

q.put(data, [block, timeout])

  • 功能:向队列存储消息
  • 参数:data(要存的内容),block(默认为True,队列满时会阻塞,设置为False则为非阻塞),timeout(阻塞等待时间)

data = q.get([block, timeout])

  • 功能:从队列中获取消息
  • 参数:block(默认为True,队列为空时会阻塞,设置为False则为非阻塞),timeout(阻塞等待时间)
  • 返回值 : 返回取出的内容

q.full()

  • 功能:判断队列是否为满
  • 返回值:布尔型,队列为满则返回True,否则为False

q.empty()

  • 功能:判断队列是否为空
  • 返回值:布尔型,队列为空则返回True,否则为False

q.qsize()

  • 功能:判断当前队列中的消息数量
  • 返回值:当前队列中存在的消息数量

q.close()

  • 功能:关闭队列
  • 在队列关闭后将不能操作队列。

运行结果:

gk@gk-vm:~/python/process_communication$ python3 queue_test2.py 
收到消息: 数据1
q.full(): False
q.empty(): False
q.qsize(): 1
消息队列已关闭
error: handle is closed

共享内存

共享内存的通信原理:

  • 在内存空开辟一块空间,对多个进程可见,进程可以写入输入,但是每次写入的内容会覆盖之前的内容。

代码实现:

from multiprocessing import Process, Value
import random

#创建共享内存
num = Value('i', 100)

# 操作共享内存增加
def num_add():
    for i in range(10):
        # 对value属性操作即操作共享内存数据
        num.value += random.randint(1,10)
# 操作共享内存减少
def num_reduce():
    for i in range(10):
        num.value -= random.randint(1,10)

p1 = Process(target = num_add)
p2 = Process(target = num_reduce)
p1.start()
p2.start()
p1.join()
p2.join()

print("result:", num.value)

其中:

obj = Value(ctype, obj)

  • 功能:开辟共享内存空间
  • 参数:ctype(要存储的数据类型,i:整数),obj(共享内存的初始化数据)
  • 返回:共享内存对象

obj.value:共享内存值,对其修改即修改共享内存

多次运行结果:

gk@gk-vm:~/python/process_communication$ python3 value_test.py 
result: 90
gk@gk-vm:~/python/process_communication$ python3 value_test.py 
result: 84
gk@gk-vm:~/python/process_communication$ python3 value_test.py 
result: 108
gk@gk-vm:~/python/process_communication$ python3 value_test.py 
result: 112
gk@gk-vm:~/python/process_communication$ python3 value_test.py 
result: 98

另外:

另外一种开辟共享内存空间的方法:

obj = Array(ctype, obj)

  • 功能:开辟共享内存空间
  • 参数:ctype(要存储的数据格式),obj(初始化存入的内容,比如列表,字符串,如果是整数则表示开辟空间的个数)
  • 返回值:返回共享内存对象
  • 可以通过遍历获取每个元素的值
  • 如果存入的是字符串,obj.value则表示字符串的首地址

实现代码:

from multiprocessing import Process,Array 
import time 

# 创建共享内存,初始放入列表
test = Array('i',[1,2,3])

# 创建共享内存,开辟9个整形空间
# test = Array('i',9)

# 存入字符串,要求bytes格式
# test = Array('c',b'abcdef')

def test1():
    test[0] = 9

def test2():
	test[1] = 8

p1 = Process(target = test1)
p2 = Process(target = test2)
p1.start()
p2.start()
p1.join()
p2.join()

for i in test:
    print(i)

运行结果:

gk@gk-vm:~/python/process_communication$ python3 value_array_test.py 
9
8
3

管道、消息队列、共享内存的对比

管道 消息队列 共享内存
开辟空间 内存 内存 内存
读写方式 两端读写(单/双向) 先进先出 覆盖之前内容
效率 一般 一般 较高
应用 多用于父子进程 广泛灵活 需要注意进行互斥操作

你可能感兴趣的:(Python)