人工智能(PythonNet)—— 进程间的通信(管道、消息队列、共享内存、信号、信号量、本地套接字)

一、进程间通信

        进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。

        由于每个进程的空间是互相独立的,进程之间无法互相直接获取彼此的资源,故引入进程间通信来实现进程间的资源交互。

        进程间通信方法:管道、消息队列、共享内存、信号、信号量、套接字。

        补充:文件类型(查看方法:ls -l 或 ll)
                b(块设备文件)
                c(字符设备文件)
                d(文件夹)   
                - (普通文件)
                l (链接文件)    link
                s  套接字文件
                p  管道文件

二、管道通信   Pipe

        在内存中开辟一段空间,形成管道结构,管道对多个进程可见,进程可以对管道进行读写操作,实现多进程之间的通信。

        from multiprocess import Pipe  # 导入库

1、Pipe函数

        fd1,fd2 = Pipe(duplex = True)
        功能:创建一个管道
        参数:默认为双向管道
                    如果设置为False,则为单向管道
        返回值:返回两个管道流对象,表示管道的两端
                      如果双向管道,fd1 fd2都可以进行读写操作
                      如果是单向管道,则fd1只可读,fd2只可写

        fd.recv()
        功能:从管道读取内容(接收消息)
        参数:无
        返回值:读到的内容(接收消息)
                说明:如果管道无内容则阻塞

        fd.send(data)
        功能: 向管道写入内容(发送消息)
        参数: 要发送的内容
                说明:如果没有接收端则管道破裂

2、示例

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()

三、消息队列 Queue

        队列存取规则: 先进先出

        在内存中开辟队列结构空间,多个进程可以向队列投放消息,在取出的时候按照存入顺序取出;任何拥有队列的进程都可以存取消息

1、Queue函数

        创建队列
        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()  关闭队列

2、示例

a、函数

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() #关闭队列

b、管道收发

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

1、函数

        obj = Value(ctype,obj)
        功能:开辟共享内存空间
        参数:ctype   字符串  要转变的c的数据类型(对照ctype表)
                    obj   共享内存的初始化数据
        返回:返回一个共享内存对象
                补充:obj.value  表示共享内存中的值。对其修改或者使用即可

        obj = Array(ctype,obj)
        功能: 开辟共享内存
        参数: ctype    要转化的c的类型
                   obj        列表,表示要存入的数据,要求列表中数类型一致
                                正整数,表示开辟一个多大的序列空间
        返回值:返回一个共享内存对象
                补充:正常操作数值,读取和赋值

2、数据类型(ctype对照表)

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

3、示例

a、共享内存 —— value

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)

b、共享内存 —— array

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)

五、信号  signal

一个进程向另一个进程通过信号传递某种讯息,接收方在接收到信号后进行相应的处理

kill  -l        查看信号
           kill  -signum  PID   给PID的进程发送一个信号

1、关于信号的概念

        信号名称:信号的名字或者数字(系统定义)
        信号含义:信号的作用(系统定义)
        默认行为:当一个进程接收到信号时采取的行为
                            终止进程,暂停进程,忽略产生

a、程序执行的同步和异步

        同步 : 按照步骤一步一步顺序执行
        异步 : 在程序执行中利用内核,不影响应用层程序持续执行
                     注: 信号属于异步通信方式,信号的发送不会影响进程的持续执行

b、常用信号

        SIGHUP   终端断开
        SIGINT   ctrl + c
        SIGQUIT  ctrl + \
        SIGTSTP  ctrl + z
        SIGKILL  终止进程且不能被处理
        SIGSTOP  暂停进程且不能被处理
        SIGALRM  时钟信号
        SIGCHLD  子进程状态改变发给父进程

2、函数

        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),这样子进程退出时会有系统自动处理

3、示例

a、signal.alarm

import signal 
import time 

#3秒后向自己发送个SIGALRM信号
signal.alarm(3)
time.sleep(2)

signal.alarm(8)

#阻塞等待接收一个信号
signal.pause()

while True:
    time.sleep(1)
    print("等待时钟.....")

b、signal.signal

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)

六、信号量    Semaphore

给定一定的信号数量,对多个进程可见,并且多个进程均可操作。
           进程根据信号量的多少,可以有不同的行为。

from multiprocessing import Semaphore

1、函数

        sem = Semaphore(num)
        功    能:定义信号量
        参    数:num:给定信号量的初始个数
        返回值:返回信号量对象

        sem.acquire()   信号量数量减1  信号量为0时会阻塞
        sem.release()   信号量数量加1
        sem.get_value()  获取当前信号量的值

2、示例

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())

七、本地套接字    socket

在linux/unix操作系统下,提供本地进程间通信的一种方式

1、本地套接字创建流程

        1.创建套接字文件
        sockfd = socket(AF_UNIX,SOCK_STREAM)
        2.绑定套接字文件
        3.监听
        4.接受发送消息

        补充:
                os.unlink(path)  or   os.remove(path)
                功能:删除某个文件
                参数:path:一个路径文件
                os.path.exists(path)
                功能:判断一个文件是否存在
                参数:path:一个路径文件

2、示例

a、本地套接字服务端

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()





b、本地套接字客户端

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)—— 目录汇总

 

你可能感兴趣的:(人工智能,PythonNet)