进程间通信

試著忘記壹切 提交于 2020-01-28 07:53:03

【进程间通信】

进程间由于空间独立,资源互相无法直接获取
此时在不同的进程间传递数据就需要专门的进程间通信方法

  • 和磁盘交互:
    使用中间文件,但是不安全,速度慢

  • 进程间通信方法(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()  
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!