进程间通信
概念
:称进程间的数据交换(共享)为进程间通信(InterProcess Communication)。
必要性
:每个进程的空间独立,它们都有自己的内存地址、数据栈 以及 其他记录进程运行状态的辅助数据,这些进程的资源不可以直接共享,若需要进程间实现数据通信,则要借助特定的手段。
进程通信的常用方法
:
multiprocessing模块
1.消息队列(from multiprocessing import Queue)
2.管道(from multiprocessing import Pipe)
3.共享内存(from multiprocessing import Value, Array)
一、消息队列
1、通信原理
主进程在内存中建立队列结构,多个进程将消息存入队列,或从队列中取出消息,以此完成进程间通信。
2、实现方法
q = Queue([maxsize=0])
功能: 初始化队列对象
参数:最多存放的消息数量;若不指定或为负数,表示可接受的消息数量没有上限(直到内存尽头)
返回值:队列对象
q.put(item, [block=True, timeout=None])
功能:将item消息写入队列
参数:item 要存入的内容
block 默认为True,设置是否阻塞 ,False为非阻塞
timeout 超时检测
注解:
若block为默认值,timeout为空,消息队列无空间可用,此时进程阻塞在写入状态,直到消息队列有空间。若设置了timeout,则等待timeout秒,若还没空间,抛出异常Queue.Full
2.timeout默认值None,表示一直等待,不会超时
3.q.put_nowait(item)相当于q.put(item, False)
q.get([block=True, timeout=None])
功能:获取队列中的一条消息,然后将其从队列中移除
参数:block 默认为True,设置是否阻塞,False为非阻塞
timeout 超时检测
返回值: 返回获取到的内容
注解:
1.若block为默认值,timeout为空,消息队列为空,此时进程阻塞在读取状态,直到从消息队列中读到消息为止。若设置了timeout,则等待timeout秒,若还没读到任何消息,抛出异常queue.Empty
2.timeout默认值None,表示一直等待,不会超时
3.q.get_nowait()相当于q.get(False)
q.full() 判断队列是否为满;满返回True,否则False
q.empty() 判断队列是否为空;空返回True,否则False
q.qsize() 获取队列中的消息数量;
q.close() 关闭队列(不允许再往队列中放入消息,一旦队列为空,所占资源回收),通常情况下无需手动处理,而是自动回收。
from multiprocessing import Queue, Process
from time import sleep
from random import randint
def write_task(q):
"""向队列中写消息"""
for i in range(20):
x = randint(0, 100)
y = randint(0, 100)
q.put((x, y))
def read_task(q):
"""从队列中读消息"""
# =====================方法一=====================
sleep(1) # 休眠1秒的目的在于,避免读进程快于写进程
while not q.empty():
sleep(0.5) # 休眠的目的在于,防止在进程运行过程中队列为空
x, y = q.get(timeout=2)
print("%d + %d = %d" % (x, y, x+y))
# =====================方法二=====================
# while True:
# sleep(0.5)
# try:
# x, y = q.get(timeout=3)
# except Exception:
# break
# else:
# print("%d + %d = %d" % (x, y, x+y))
if __name__ == "__main__":
q = Queue(5) # 创建消息队列
pw = Process(target=write_task, args=(q, ))
pr = Process(target=read_task, args=(q, ))
pw.start()
pr.start()
pw.join()
pr.join()
二、管道(pipe)通信
1、通信原理
主进程在内存中开辟管道空间,并生成管道操作对象,多个进程再对管道操作对象进行写入或读取,以此完成进程间的通信。
2、实现方法
fd1, fd2 = Pipe(duplex=True)
功能:创建管道
参数:默认情况True表示双工(双向);若duplex=False表示单向管道
返回值:两个连接对象,分别表示管道两端,每个连接对象都有send()和recv()方法(相互之间的),也可通俗理解为两个读写对象
若是双向管道,则均可读写
若是单向管道,则fd1只读(只能用于接收消息),fd2只写(只能用于发送消息)
fd1.recv()
功能:从管道中获取数据并返回,该方法会一直阻塞直到接收到数据
fd1.send(data)
功能:想管道中写入数据,发送的数据对象必须是可序列化的,当数据量过大时(超过32MB),有可能引发ValueError异常。
fd1.poll([timeout])
功能:返回连接对象中是否有可以读取的数据
from multiprocessing import Process, Pipe
import os, time
# 单向管道中fd1只能读,fd2只能写
def fun(name, fd2):
# 子进程向管道中写入内容
fd2.send({name: os.getpid()})
if __name__ == "__main__":
fd1, fd2 = Pipe(False) # 创建管道
jobs = []
for i in range(5):
p = Process(target=fun, args=(i, fd2))
jobs.append(p)
p.start()
for i in range(5):
# 父进程读取管道
data = fd1.recv()
print(data)
for i in jobs:
i.join()
三、共享内存
1、通信原理
在共享内存上创建可被子进程继承的共享对象——ctypes对象。在内存中开辟空间,进程们在此空间上写入或读取内容,以完成进程间的通信。不同于消息队列和管道,共享内存没有对内存作结构化调整,只记录一个起始地址,每次写入的内容会覆盖之前的内容。
2、实现方法
obj = Value(ctype, data, lock=True)
功能:开辟共享内存空间
参数:ctype 表示共享内存的空间类型,如’i’,‘f’,‘c’,更多参数请参见下表
data 共享内存空间的初始数据内容
lock 递归锁,默认True,用于同步对于此值的访问操作
返回值:共享内存对象
obj.value 共享内存对象的属性,查看或修改该属性可对共享内存进行读写
类型吗
C语言类型
Python类型
所占字节数(至少)
‘c’
char
str(单字符的字符串)
1
‘b’
signed char
int
1
‘B’
unsigned char
int
1
‘u’
Py_UNICODE
str(unicode码的单字符字符串)
2
‘h’
signed short
int
2
‘H’
unsigned short
int
2
‘i’
signed int
int
2
‘I’
unsigned int
long
2
‘l’
signed long
int
4
‘L’
unsigned long
long
4
‘f’
float
float
4
‘d’
double
float
8
from multiprocessing import Value, Process
import time
import random
# 操作共享内存
def boy(money):
for i in range(30):
time.sleep(0.2)
money.value += random.randint(1, 1000)
def girl(money):
for i in range(30):
time.sleep(0.15)
money.value -= random.randint(100, 800)
if __name__ == "__main__":
# 创建共享内存
money = Value('i', 3000)
m = Process(target=boy, args=(money,))
g = Process(target=girl, args=(money, ))
m.start()
g.start()
m.join()
g.join()
# 获取共享内存值
print("一个月的余额:", money.value)
obj = Array(ctype, data, lock=True)
功能开辟共享内存空间
参数:ctype 表示共享内存的空间类型,如’i’,‘f’,‘c’,更多参数请参见上表
data 若是整数,则表示开辟空间的大小;
若是其他数据类型(一般为Python序列),则表示开辟空间存放的初始化数据
返回值:返回可迭代的共享内存对象
from multiprocessing import Process, Array
def fun(shm):
# 共享内存对象是可迭代的对象
for i in shm:
print(i)
# 替换共享内存中的值
# shm[1] = 1000
shm[0] = b'H'
if __name__ == "__main__":
# 创建共享内存
# 共享内存开辟5个整型列表空间
# shm = Array('i', 5) # 默认初始化值都为0
# shm[0] = 1;shm[1] = 2;shm[2] = 3;shm[3] = 4;shm[4] = 5 # 通过索引修改任意值
# 共享内存初始化整型数组数据[1,2,3]
# shm = Array('i', [1, 2, 3])
# 字节串
shm = Array('c', b'hello')
p = Process(target=fun, args=(shm, ))
p.start()
p.join()
# 父进程中查看对应的值
for i in shm:
print(i, end=" ")
print()
# 通过value属性访问字节串,且只能访问访问字节串
print(shm.value)
print(shm.raw)