【进程间通信】
进程间由于空间独立,资源互相无法直接获取
此时在不同的进程间传递数据就需要专门的进程间通信方法
-
和磁盘交互:
使用中间文件,但是不安全,速度慢 -
进程间通信方法(IPC):
管道、消息队列、共享内存、信号、信号量、套接字
【管道通信Pipe】
1.原理:
在内存中开辟一块空间,形成管道结构,管道对多个进程可见,进程可以通过对管道的读写操作进行通信
2.[multiprocessing.Pipe]
1.fd1,fd2 = Pipe(duplex=True)
功能:创建一个管道
参数:
duplex默认为True 表示双向管道
设置为False 表示单项管道
返回值:
返回两个管道流对象,表示管道两端
如果是双向管道,则都可以读写
如果是单向管道,则fd1只读 fd2只写
2.fd1.recv()
功能:从管道内读取信息
参数:无
返回值:读到的内容
注意:当管道内无内容的时候会阻塞
3.fd2.send(data)
功能:向管道写入内容
参数:要写的内容
注意:可以发送几乎python的任意数据类型
3.示例(双向管道):
from multiprocessing import Process,Pipe
import os,time
# 创建管道
fd1,fd2 = Pipe()
def fun(name):
time.sleep(3)
# 向管道内写入内容
fd2.send('hello' + str(name))
jobs = []
for i in range(5):
p = Process(target=fun,args=(i,))
jobs.append(p)
p.start()
for i in range(5):
# 读取管道内消息
data = fd1.recv()
print(data)
for i in jobs:
i.join()
---------------------------------------
hello0
hello1
hello2
hello4
hello3
(当设置为单向管道的时候,注意fd1和fd2的顺序,fd1是只读,fd2是只写)
【消息队列】
1.队列特征:先进先出
在内存中开辟队列结构空间,对多个进程可见。多个进程向队列存入消息,取出消息,完成进程间通信
2.创建队列:
from multiprocessing import Queue
1.q = Queue(maxsize=0)
功能:创建队列
参数:maxsize默认表示根据系统分配空间存储消息
如果传入一个正整数则表示最多存入消息数量
返回值:队列对象
2.q.put(data[,block,timeout])
功能:向队列中存入消息
参数:
data:存入的数据(支持python数据类型)
block:默认为True,表示当队列满时阻塞
设置为False,表示非阻塞
timeout:当block为True时,表示超时时间
3.data = q.get([block,timeout])
功能:从队列获取消息
参数:
block:默认为True,表示当队列空时阻塞
设置为False,表示非阻塞
timeout:当block为True时,表示超时时间
返回值:返回获取到的消息
4.q.full()
功能:判断队列是否为满,满返回True
5.q.empty()
功能:判断队列是否为空,空返回True
6.q.qsize()
功能:获取队列中消息数量,(得到当前队列中消息的个数)
7.q.close()
功能:关闭队列
3.示例:
from multiprocessing import Queue,Process
import time
# 创建消息队列
q = Queue()
def fun1():
time.sleep(1)
# q.put('我是进程1')
q.put({'a':1,'b':2})
def fun2():
time.sleep(2)
print('收到消息:',q.get())
p1 = Process(target=fun1)
p2 = Process(target=fun2)
p1.start()
p2.start()
p1.join()
p2.join()
在终端打印:
收到消息: {'a': 1, 'b': 2}
------------------------------------
示例2:
from multiprocessing import Queue
# 创建队列
q = Queue(3)
q.put(1)
print(q.full())
q.put(2)
q.put(3)
print(q.full())
# 设置超时时间为3秒
q.put(4, timeout=3)
print(q.get())
print('队列中还有%d条消息' % q.qsize())
print(q.empty())
q.close() # 关闭队列
【共享内存】
在内存中开辟一段空间,存储数据,对多个进程可见。
每次写入共享内存的数据会覆盖之前的内容。
1.创建共享内存:
from multiprocessing import Value, Array
2.obj = value(ctype,obj)
功能:开辟共享内存空间
参数:ctype:字符串 要转变的c语言类型code (对照ctype表)
obj:共享内存的初始值
返回值:共享内存对象
obj.value:表示共享内存中的值,对其修改或者使用即为使用共享内存中的值
3.示例:
from multiprocessing import Process,Value
import time
import random
# 共享内存对象初始存放2000
money = Value('i',2000)
# 存钱进程
def deposite():
for i in range(100):
time.sleep(0.05)
# 对value属性的操作实际就是在操作共享内存
money.value += random.randint(1,200)
# 取钱进程
def withdraw():
for i in range(100):
time.sleep(0.04)
# 对value属性的操作实际就是在操作共享内存
money.value -= random.randint(1,200)
d = Process(target=deposite)
w = Process(target=withdraw)
d.start()
w.start()
d.join()
w.join()
print(money.value)
4.obj = Array(ctype,obj)
功能:开辟共享内存空间
参数:
ctype:要转换的类型
obj:要存入共享内存的数据
列表 将列表存入共享内存(要求列表中数据类型一致)
整数 在共享内存中开辟几个单元的空间
返回值:返回一个共享内存对象
5.示例:
from multiprocessing import Process,Array
import time
# 创建共享内存,列表是共享内存的初始值
# shm = Array('i',[1,2,3,4,5])
# 表示在共享内存中开辟5个整形空间
shm = Array('i',5)
def fun():
for i in shm:
print(i)
# 修改共享内存的内容
shm[3] = 1000
p = Process(target=fun)
p.start()
p.join()
for i in shm:
print(i)
【总结】
管道,消息队列,共享内存三种进程方式间对比:
管道 消息队列 共享内存
开辟空间 内存 内存 内存
读写方式 双向/单项 先进先出 操作覆盖内存
效率 一般 一般 较快
应用 多用于父子进程 方便灵活广泛 较复杂
是否需要互斥机制 否 否 需要
【信号】
(信号是唯一的异步通信方式)
一个进程向另一个进程通过信号传递某种信息。
接收方在接受到信号时进行相应的处理。
终端命令:
kill -l 查看信号
kill -signame PID 给PID的进程发送一个信号
1.关于信号:
1.信号名称:
系统定义,名字或者前面代表数字
2.信号含义:
系统定义,描述信号的作用
3.默认处理方法:
当一个进程接收到信号时默认产生的效果
2.需要记住的信号,常用的
【SIGHUP】
含义:
该信号在用户终端连接(正常或非正常)结束时发出,通常是在终端的控制进程结束时,
通知同一会话内的各个作业与控制终端不在关联。
默认操作:终止
【SIGINT】
含义:
该信号在用户键入INTR字符(通常是Ctrl-C)时发出,终端驱动程序发送此信号
并送到前台进程中的每一个进程。
默认操作:终止
【SIGQUIT】
含义:
该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-\)来控制
【SIGILL】
含义:该信号在一个进程企图执行一条非法指令时(可执行文件本身出现错误,
或者试图执行数据段,堆栈溢出时)发出
默认操作:终止
【SIGFPE】
含义:
该信号在发生致命的算术运算错误时发出。这里不仅包括浮点运算错误,
还包括溢出及除数为0等其他所有的算术的错误。
默认操作:终止
【SIGKILL】
含义:
该信号用来立即结束程序的运行,并且不能被阻塞,处理和忽略
默认操作:终止
【SIGALRM】
含义:
该信号当一个定时器到时的时候发出
默认操作:终止
【SIGSTOP】
含义:
该信号用于暂停一个进程,且不能被阻塞,处理或忽略
默认操作:暂停进程
【SIGTSTP】
含义:
该信号用于暂停交互进程,用户可键入SUSP字符
(通常是Ctrl-Z)发出这个信号
默认操作:暂停进程
【SIGCHLD】
含义:
子进程改变状态时,父进程会收到这个信号
默认操作:忽略
【SIGABORT】
含义:该信号用于结束进程
默认操作:终止
3.python如何操作信号:
os.kill(pid,sig)
功能:发送信号给某个进程
参数:
pid:给哪个进程发送信号
sig:要发送什么信号
示例:
import os
import signal
# 向24051进程发送SIGKILL信号
os.kill(24051,signal.SIGKILL)
signal.alarm(sec)
功能:一定时间后向自身发送一个SIGALARM信号
参数:定时时间(sec 时钟秒数)
程序执行的异步和同步:
同步:按照步骤一步一步往下顺序执行
异步:在程序执行中利用内核,不影响应用层的持续执行
注意:
信号属于异步通信方式,信号的发送不会影响进程的持续执行
一个进程中只允许有一个时钟,设置第二个时钟时会重置
signal.pause()
功能:阻塞等待进程收到一个信号
示例:
import signal
import time
# 3秒后向自己发送个SIGALRMXIN信号
signal.alarm(3)
# 阻塞等待一个信号
signal.pause()
while True:
time.sleep(1)
print('等待时钟……')
---------------------------------------
4. 信号处理:
signal.signal(signum,handler)
功能:处理信号
参数:
sigunm:要处理的信号
handler:信号的处理方法
处理方法(可选值):
SIG_DFL :表示使用默认方法处理
SIG_IGN:表示忽略这个信号
func:自定义函数
func函数格式需求:
def func(sig,frame):
pass
sig:接收到的信号
frame:信号结构对象
总结:
1.signal函数是一个异步处理函数
2.signal函数不能处理SIGKILL,SIGSTOP信号
3.在父进程加上:signal(SIGCHLD,SIG_IGN),当子进程退出时会自动交由系统处理(防止僵尸进程)
示例1:
import signal
from time import sleep
signal.alarm(5)
# 使用默认方法处理SIGALRM
# signal.signal(signal.SIGALRM,signal.SIG_DFL)
# 使用忽略的方法处理SIGALRM
signal.signal(signal.SIGALRM,signal.SIG_IGN)
# 忽略ctrl-c
signal.signal(signal.SIGINT,signal.SIG_IGN)
while 1:
sleep(2)
print('让你摁ctrl-c')
print('等待时钟...')
示例2:
from signal import *
import time
# 信号处理函数
def handler(sig,frame):
if sig == SIGALRM:
print('接收到时钟信号')
elif sig == SIGINT:
print('就不结束 ')
alarm(5)
# 当接收到SIGALRM信号时,用handler函数处理
signal(SIGALRM,handler)
signal(SIGINT,handler)
while 1:
print('Warting for a singal')
time.sleep(2)
【信号量】
1.定义:
给定一定的数量,对多个进程可见,并且多个进程根据信号的数量多少确定不同的行为
2.方法:
multiprocessing—>Semaphore()
sem = Semaphore(num)
功能:生成信号量对象
参数:信号量的初始值
返回:信号量对象
sem.acquire() 将信号量数量减一 ,信号量为0会阻塞
sem.release() 将信号量数量加一
sem.get_value() 获取当前信号量的值
3.cookie:
1、multiprocessing.current_process()
获取当前进程对象
2、使用信号处理僵尸进程
在父进程中,忽略子进程的发送信号
signal(SIGCHLD,SIG_IGN)
3、示例:
from multiprocessing import Semaphore, Process, current_process
from time import sleep
# 创建信号量初始值为3
sem = Semaphore(3)
def fun():
print('进程%s等待信号量' % current_process())
# 第四个进程无信号资源会阻塞
sem.acquire()
print('进程%s消耗信号量' % current_process())
sleep(3)
print('进程%s添加信号量' % current_process())
sem.release()
jobs = []
for i in range(4):
p = Process(target=fun)
p.start()
jobs.append(p)
for i in jobs:
i.join()
来源:CSDN
作者:雨醉东风
链接:https://blog.csdn.net/zhangxuelong461/article/details/104059362