進程間通信(IPC)
1. 必要性: 進程間空間獨立,資源不共享,此時在需要進程間數據傳輸時就需要特定的手段進行數據通信。
2. 常用進程間通信方法
管道 消息隊列 共享內存 信號 信號量 套接字
管道通信(Pipe)
1. 通信原理
在內存中開闢管道空間,生成管道操作對象,多個進程使用同一個管道對象進行讀寫即可實現通信
2. 實現方法
from multiprocessing import Pipe
fd1,fd2 = Pipe(duplex = True)
功能: 創建管道
參數:默認表示雙向管道
如果爲False 表示單向管道
返回值:表示管道兩端的讀寫對象
如果是雙向管道均可讀寫
如果是單向管道fd1只讀 fd2只寫
fd.recv()
功能 : 從管道獲取內容
返回值:獲取到的數據
fd.send(data)
功能: 向管道寫入內容
參數: 要寫入的數據
示例:
# 管道通信
import os
import time
from multiprocessing import Pipe, Process
def fun(fd, name):
time.sleep(2)
fd.send({name: os.getpid()})
if __name__ == '__main__':
# 創建雙向管道
fd1, fd2 = Pipe(duplex=True)
jobs = []
# 生成三個進程給管道寫入信息
for i in range(3):
p = Process(target=fun, args=(fd1, i))
jobs.append(p)
p.start()
# 從管道中讀取數據
for i in range(3):
data = fd2.recv()
print(data)
for i in jobs:
i.join()
消息隊列
1.通信原理
在內存中建立隊列模型,進程通過隊列將消息存入,或者從隊列取出完成進程間通信。
2. 實現方法
from multiprocessing import Queue
q = Queue(maxsize=0)
功能: 創建隊列對象
參數:最多存放消息個數
返回值:隊列對象
q.put(data,[block,timeout])
功能:向隊列存入消息
參數:data 要存入的內容
block 設置是否阻塞 False爲非阻塞,默認爲True,阻塞
timeout 超時檢測
q.get([block,timeout])
功能:從隊列取出消息
參數:block 設置是否阻塞 False爲非阻塞
timeout 超時檢測
返回值: 返回獲取到的內容
q.full()
判斷隊列是否爲滿
q.empty()
判斷隊列是否爲空
q.qsize()
獲取隊列中消息個數
q.close()
關閉隊列
示例:
# 消息隊列通信
from multiprocessing import Queue, Process
from time import sleep
from random import randint
def request(q):
for i in range(20):
x = randint(0, 100)
y = randint(0, 100)
# 默認爲阻塞,當管道數據滿時,會一直等待別的線程取走數據纔會存入新數據
# 如果設置非阻塞,當管道數據滿時會拋出異常:exception Queue.Full
q.put((x, y))
def handle(q):
while True:
sleep(0.5)
try:
# 默認爲阻塞,當管道數據爲空時,會一直等待別的線程寫入數據纔會取出新數據
# 如果設置非阻塞,當管道數據爲空時會拋出異常:exception Queue.Empty
x, y = q.get(timeout=3) # 最多等待3秒,超時會拋異常
except:
break
else:
print("%d + %d = %d" % (x, y, (x + y)))
if __name__ == '__main__':
# 創建隊列對象,可存放三個數據
q = Queue(3)
# 創建一個進程把數據存入隊列(20個元組)
p1 = Process(target=request, args=(q,))
# 創建一個進程從隊列中取值
p2 = Process(target=handle, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
共享內存
1. 通信原理:
在內中開闢一塊空間,進程可以寫入內容和讀取內容完成通信,但是每次寫入內容會覆蓋之前內容。
2. 實現方法
數據類型
from multiprocessing import Value,Array
obj = Value(ctype,data)
功能 : 開闢共享內存
參數 : ctype 表示共享內存空間類型 'i' 'f' 'c'
data 共享內存空間初始數據
返回值:共享內存對象
obj.value 對該屬性的修改查看即對共享內存讀寫
obj = Array(ctype,data)
功能: 開闢共享內存空間
參數: ctype 表示共享內存數據類型
data 整數則表示開闢空間的大小,其他數據類型表示開闢空間存放的初始化數據
返回值:共享內存對象
Array共享內存讀寫: 通過遍歷obj可以得到每個值,直接可以通過索引序號修改任意值。
* 可以使用obj.value直接打印共享內存中的字節串
示例1:Value
from multiprocessing import Process, Value
import time
import random
# 操作共享內存
def man(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', 5000)
# 兩個線程共同影響共享內存
p1 = Process(target=man, args=(money,))
p2 = Process(target=girl, args=(money,))
p1.start()
p2.start()
p1.join()
p2.join()
print("剩餘:", money.value)
示例2:Array
from multiprocessing import Process, Array
# 創建共享內存
# 共享內存開闢5個整型列表空間
# shm = Array('i', 5) 參數2整數代表空間大小
def fun(shm):
# 共享內存對象可迭代
for c in shm:
print(c)
# 修改內存空間數據
shm[1] = b'o'
if __name__ == '__main__':
# shm = Array('i', [1, 2, 3]) 參數2直接傳入數據
shm = Array('c', b'hello')
p1 = Process(target=fun, args=(shm,))
p1.start()
p1.join()
# 迭代取值
for i in shm:
print(i)
# 一次性取值
print(shm.value)
信號量(信號燈集)
1. 通信原理
給定一個數量的信號對多個進程可見。多個進程都可以操作該信號量增減,並根據數量值決定自己的行爲。
2. 實現方法
from multiprocessing import Semaphore
sem = Semaphore(num)
功能 : 創建信號量對象
參數 : 信號量的初始值
返回值 : 信號量對象
sem.acquire()
將信號量減1 當信號量爲0時阻塞
sem.release()
將信號量加1
sem.get_value()
獲取信號量數量
示例:
"""
信號量演示
注意: 信號量相當於資源,多個進程對數量進行控制
"""
from multiprocessing import Process, Semaphore
from time import sleep
import os
# 任務函數
def handle(s):
print("現存信號:", s.get_value())
s.acquire() # 執行任務必須消耗一個信號量
print("開始執行任務:", os.getpid())
sleep(2)
print("執行任務結束:", os.getpid())
s.release() # 增加一個信號量
if __name__ == '__main__':
# 創建信號量
sem = Semaphore(3)
# 創建5個線程,三個信號肯定不夠,前三個線程將信號量兼爲0,
# 後兩個線程執行acquire時會魚洞阻塞,
# 等待前三個中有人將信號還回纔可以繼續執行
for i in range(5):
p = Process(target=handle, args=(sem,))
p.start()