进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
由于每个进程的空间是互相独立的,进程之间无法互相直接获取彼此的资源,故引入进程间通信来实现进程间的资源交互。
进程间通信方法:管道、消息队列、共享内存、信号、信号量、套接字。
补充:文件类型(查看方法:ls -l 或 ll)
b(块设备文件)
c(字符设备文件)
d(文件夹)
- (普通文件)
l (链接文件) link
s 套接字文件
p 管道文件
在内存中开辟一段空间,形成管道结构,管道对多个进程可见,进程可以对管道进行读写操作,实现多进程之间的通信。
from multiprocess import Pipe # 导入库
fd1,fd2 = Pipe(duplex = True)
功能:创建一个管道
参数:默认为双向管道
如果设置为False,则为单向管道
返回值:返回两个管道流对象,表示管道的两端
如果双向管道,fd1 fd2都可以进行读写操作
如果是单向管道,则fd1只可读,fd2只可写
fd.recv()
功能:从管道读取内容(接收消息)
参数:无
返回值:读到的内容(接收消息)
说明:如果管道无内容则阻塞
fd.send(data)
功能: 向管道写入内容(发送消息)
参数: 要发送的内容
说明:如果没有接收端则管道破裂
from multiprocessing import Process,Pipe
import os,time
#如果参数为False则fd1只能recv fd2只能send
# fd1,fd2 = Pipe(False)
#创建一个双向管道
fd1,fd2 = Pipe()
# fd1.close()
def fun(name):
time.sleep(3)
#发字符串到管道
fd2.send("hello " + str(name))
print(os.getppid(),"----",os.getpid())
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()
队列存取规则: 先进先出
在内存中开辟队列结构空间,多个进程可以向队列投放消息,在取出的时候按照存入顺序取出;任何拥有队列的进程都可以存取消息
创建队列
q = Queue(maxsize = 0)
功能 : 创建队列
参数 : maxsize 默认为0,表示根据系统分配空间存储消息
如果传入一个正整数,表示最多存放多少条消息
返回 : 消息队列对象
q.put(data, [block, timeout])
功能: 存放消息
参数: data 存入的消息 (python数据类型)
block 默认为True表示当队列满的时候阻塞
设置为False则表示非阻塞
timeout 当block为True表示超时时间
data = q.get([block, timeout])
功能 : 取出消息
参数 : block 默认为True 当队列空时阻塞
设置为False表示非阻塞
timeout 当block为True时表示超时时间
返回值 : 返回获取的消息
q.full() 判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 判断当前队列有多少消息
q.close() 关闭队列
from multiprocessing import Queue
from time import sleep
#创建队列
q = Queue(3)
q.put(1)
print(q.full())
q.put(2)
q.put(3)
print(q.full())
# 设置超时事件为3sec
# q.put(4,True,3)
print(q.get())
print("队列中还有%d条消息"%q.qsize())
print(q.empty())
q.close() #关闭队列
from multiprocessing import Process,Queue
import time
#创建消息队列
q = Queue()
def fun1():
time.sleep(1)
q.put("我是进程1")
def fun2():
time.sleep(2)
print("取消息:",q.get())
p1 = Process(target = fun1)
p2 = Process(target = fun2)
p1.start()
p2.start()
p1.join()
p2.join()
在内存中开辟一段空间,存储数据,对多个进程可见。每次写入共享内存中的数据会覆盖之前的内容。由于对内存格式化较少,所以存取速度快。
from multiprocessing import Value,Array
obj = Value(ctype,obj)
功能:开辟共享内存空间
参数:ctype 字符串 要转变的c的数据类型(对照ctype表)
obj 共享内存的初始化数据
返回:返回一个共享内存对象
补充:obj.value 表示共享内存中的值。对其修改或者使用即可
obj = Array(ctype,obj)
功能: 开辟共享内存
参数: ctype 要转化的c的类型
obj 列表,表示要存入的数据,要求列表中数类型一致
正整数,表示开辟一个多大的序列空间
返回值:返回一个共享内存对象
补充:正常操作数值,读取和赋值
Type code | C Type | Python Type | Minimum size in bytes | Notes |
---|---|---|---|---|
'b' |
signed char | int | 1 | |
'B' |
unsigned char | int | 1 | |
'u' |
Py_UNICODE | Unicode character | 2 | (1) |
'h' |
signed short | int | 2 | |
'H' |
unsigned short | int | 2 | |
'i' |
signed int | int | 2 | |
'I' |
unsigned int | int | 2 | |
'l' |
signed long | int | 4 | |
'L' |
unsigned long | int | 4 | |
'q' |
signed long long | int | 8 | (2) |
'Q' |
unsigned long long | int | 8 | (2) |
'f' |
float | float | 4 | |
'd' |
double | float | 8 |
参考来源:https://docs.python.org/3.6/library/array.html
from multiprocessing import Value,Process
import time
import random
#向共享内存存钱
def deposite(money):
for i in range(100):
time.sleep(0.03)
money.value += random.randint(1,200)
#从共享内存取钱
def withdraw(money):
for i in range(100):
time.sleep(0.02)
money.value -= random.randint(1,150)
#创建共享内存对象
money = Value('i',2000)
d = Process(target = deposite,args = (money,))
w = Process(target = withdraw,args = (money,))
d.start()
w.start()
d.join()
w.join()
#查看共享内存数据
print(money.value)
from multiprocessing import Array,Process
import time
def fun(shm):
for i in shm:
print(i)
shm[2] = 1000
#开辟共享内存空间,可容纳6个整数
#初始值是[1,2,3,4,5,6]
# shm = Array('i',[1,2,3,4,5,6])
#表示在共享内存中开辟一个包含6个整形的空间
shm = Array('i',6)
p = Process(target = fun,args = (shm,))
p.start()
p.join()
for i in shm:
print(i)
一个进程向另一个进程通过信号传递某种讯息,接收方在接收到信号后进行相应的处理
kill -l 查看信号
kill -signum PID 给PID的进程发送一个信号
信号名称:信号的名字或者数字(系统定义)
信号含义:信号的作用(系统定义)
默认行为:当一个进程接收到信号时采取的行为
终止进程,暂停进程,忽略产生
同步 : 按照步骤一步一步顺序执行
异步 : 在程序执行中利用内核,不影响应用层程序持续执行
注: 信号属于异步通信方式,信号的发送不会影响进程的持续执行
SIGHUP 终端断开
SIGINT ctrl + c
SIGQUIT ctrl + \
SIGTSTP ctrl + z
SIGKILL 终止进程且不能被处理
SIGSTOP 暂停进程且不能被处理
SIGALRM 时钟信号
SIGCHLD 子进程状态改变发给父进程
os.kill(pid,sig)
功能 : 发送信号给某个进程
参数 : pid 给哪个进程发送信号
sig 要发送什么信号
signal.alarm(sec)
功能 : 一定时间后给自身发送一个 SIGALRM信号
参数 : 指定时间
注:一个进程中只能同时有一个时钟,后面的时钟时间会覆盖前面的
signal.pause()
功能: 阻塞等待一个信号的发生
signal.signal(signum,handler)
功能 : 处理信号
参数 : signum : 要处理的信号
handler : 信号的处理方法
SIG_DFL 表示使用默认的方法处理
SIG_IGN 表示忽略这个信号
func 自定义函数处理信号
def func(sig,frame):
pass
sig : 表示要处理的信号
frame : 信号的结构对象
说明:
a、signal函数是一个异步处理函数
b、signal函数不能处理 SIGKILL SIGSTOP信号
c、在父进程中使用signal(SIGCHLD,SIG_IGN),这样子进程退出时会有系统自动处理
import signal
import time
#3秒后向自己发送个SIGALRM信号
signal.alarm(3)
time.sleep(2)
signal.alarm(8)
#阻塞等待接收一个信号
signal.pause()
while True:
time.sleep(1)
print("等待时钟.....")
from signal import *
import time
#信号处理函数
def handler(sig,frame):
if sig == SIGALRM:
print("收到时钟信号")
elif sig == SIGINT:
print("就不退出")
alarm(5)
#信号使用handler函数处理
signal(SIGALRM,handler)
signal(SIGINT,handler)
while True:
print("Waiting for a signal")
time.sleep(2)
给定一定的信号数量,对多个进程可见,并且多个进程均可操作。
进程根据信号量的多少,可以有不同的行为。
from multiprocessing import Semaphore
sem = Semaphore(num)
功 能:定义信号量
参 数:num:给定信号量的初始个数
返回值:返回信号量对象
sem.acquire() 信号量数量减1 信号量为0时会阻塞
sem.release() 信号量数量加1
sem.get_value() 获取当前信号量的值
from multiprocessing import Semaphore,Process,current_process
from time import sleep
#创建信号量初始值为3
sem = Semaphore(3)
def fun():
print("进程 %s 等待信号量"%current_process())
#第四个进程无信号资源会阻塞
sem.acquire() #信号量 -1
print("进程 %s 消耗信号量"%current_process())
sleep(3)
print("进程 %s 添加信号量"%current_process())
sem.release() #信号量 +1
jobs = []
for i in range(4):
p = Process(target = fun)
p.start()
jobs.append(p)
for i in jobs:
i.join()
#获取信号量数量
print(sem.get_value())
在linux/unix操作系统下,提供本地进程间通信的一种方式
1.创建套接字文件
sockfd = socket(AF_UNIX,SOCK_STREAM)
2.绑定套接字文件
3.监听
4.接受发送消息
补充:
os.unlink(path) or os.remove(path)
功能:删除某个文件
参数:path:一个路径文件
os.path.exists(path)
功能:判断一个文件是否存在
参数:path:一个路径文件
from socket import *
import sys,os
#确定用哪个文件进行通信
server_address = './test'
#判断文件存在性,如果已经存在需要处理
if os.path.exists(server_address):
os.unlink(server_address)
#创建本地套接字
sockfd = socket(AF_UNIX,SOCK_STREAM)
#绑定本地套接字文件
sockfd.bind(server_address)
#监听
sockfd.listen(5)
#收发消息
while True:
c,addr = sockfd.accept()
while True:
data = c.recv(1024)
if data:
print(data.decode())
c.sendall("收到消息".encode())
else:
break
c.close()
s.close()
from socket import *
import sys
#文件已经被另一端创建
# 需要和另一端使用同一个socket文件
server_address = "./test"
try:
sockfd = socket(AF_UNIX,SOCK_STREAM)
sockfd.connect(server_address)
except error as e:
print(e)
sys.exit(1)
while True:
message = input("Your message>>")
if message:
sockfd.sendall(message.encode())
print(sockfd.recv(1024).decode())
else:
break
sockfd.close()
八、附录:目录
人工智能(PythonNet)—— 目录汇总