Python多任务之进程间通信

进程间通信

概念:称进程间的数据交换(共享)为进程间通信(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 超时检测
注解:

  1. 若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’charstr(单字符的字符串)1
‘b’signed charint1
‘B’unsigned charint1
‘u’Py_UNICODEstr(unicode码的单字符字符串)2
‘h’signed shortint2
‘H’unsigned shortint2
‘i’signed intint2
‘I’unsigned intlong2
‘l’signed longint4
‘L’unsigned longlong4
‘f’floatfloat4
‘d’doublefloat8
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)
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值