互斥鎖
進程之間數據隔離, 但是多個進程可以共享同一塊數據,比如共享同一套文件系統,所以訪問同一個文件,或同一個打印終端,是沒有問題的,而共享帶來的是競爭,競爭帶來的結果就是錯亂,如下
from multiprocessing import Process
import time
import os
def task(name):
print('%s 上廁所 [%s]' %(name ,os.getpid()))
time.sleep(1)
print('%s 上完廁所 [%s]' %(name ,os.getpid()))
if __name__ == '__main__':
for i in range(3):
p=Process(target=task, args=('進程%s' %i,))
p.start()
----------輸出結果
進程2 上廁所 [8076]
進程0 上廁所 [5176]
進程1 上廁所 [2100]
進程2 上完廁所 [8076]
進程0 上完廁所 [5176]
進程1 上完廁所 [2100]
如何控制,就是加鎖處理。而互斥鎖的意思就是互相排斥,如果把多個進程比喻為多個人,互斥鎖的工作原理就是多個人都要去爭搶同一個資源:衛生間,一個人搶到衛生間后上一把鎖,
其他人都要等着,等到這個完成任務后釋放鎖,其他人才有可能有一個搶到......所以互斥鎖的原理,就是把並發改成串行,降低了效率,但保證了數據安全不錯亂
from multiprocessing import Process, Lock
import time
import os
def task(name,mutex):
mutex.acquire()
print('%s 上廁所 [%s]' %(name ,os.getpid()))
time.sleep(1)
print('%s 上完廁所 [%s]' %(name ,os.getpid()))
mutex.release()
if __name__ == '__main__':
mutex = Lock()
for i in range(3):
p=Process(target=task, args=('進程%s' %i,mutex))
p.start()
----輸出結果
進程0 上廁所 [5340]
進程0 上完廁所 [5340]
進程1 上廁所 [7796]
進程1 上完廁所 [7796]
進程2 上廁所 [7860]
進程2 上完廁所 [7860]
模擬搶票練習
多個進程共享同一文件,我們可以把文件當數據庫,用多個進程模擬多個人執行搶票任務
# db.txt
# {"count": 2}
from multiprocessing import Process,Lock
import json
import time
import os
def query_ticket(name):
time.sleep(1)
with open('db.txt','r',encoding="utf-8") as f:
d = json.load(f)
print('[%s] 查看到剩余票數 [%s]'% (name, d['count']))
def buy_ticket(name):
time.sleep(1)
with open('db.txt','r', encoding="utf-8") as f:
d = json.load(f)
if d.get('count') >0 :
d['count'] -= 1
time.sleep(1)
json.dump(d, open('db.txt','w',encoding='utf-8'))
print('<%s> 購票成功' % name)
else:
print('沒有多余的票,<%s> 購票失敗' % name)
def task(name,mutex):
query_ticket(name)
mutex.acquire()
buy_ticket(name)
mutex.release()
if __name__ == '__main__':
mutex = Lock()
print('開始搶票了……')
for i in range(5):
p = Process(target=task, args=('進程%s' %i,mutex))
p.start()
輸出結果
開始搶票了…… [進程0] 查看到剩余票數 [2] [進程1] 查看到剩余票數 [2] [進程2] 查看到剩余票數 [2] [進程4] 查看到剩余票數 [2] [進程3] 查看到剩余票數 [2] <進程0> 購票成功 <進程1> 購票成功 沒有多余的票,<進程2> 購票失敗 沒有多余的票,<進程4> 購票失敗 沒有多余的票,<進程3> 購票失敗
互斥鎖與join
使用join可以將並發變成串行,互斥鎖的原理也是將並發變成串行,那我們直接使用join就可以了啊,為何還要互斥鎖
from multiprocessing import Process,Lock
import json
import time
def query_ticket(name):
time.sleep(1)
with open('db.txt','r',encoding="utf-8") as f:
d = json.load(f)
print('[%s] 查看到剩余票數 [%s]'% (name, d['count']))
def buy_ticket(name):
time.sleep(1)
with open('db.txt','r', encoding="utf-8") as f:
d = json.load(f)
if d.get('count') > 0 :
d['count'] -= 1
time.sleep(1)
json.dump(d, open('db.txt','w',encoding='utf-8'))
print('<%s> 購票成功' % name)
else:
print('沒有多余的票,<%s> 購票失敗' % name)
def task(name):
query_ticket(name)
buy_ticket(name)
if __name__ == '__main__':
print('開始搶票了……')
for i in range(5):
p = Process(target=task, args=('進程%s' % i,))
p.start()
p.join()
----------------輸出結果--------------
開始搶票了……
[進程0] 查看到剩余票數 [2]
<進程0> 購票成功
[進程1] 查看到剩余票數 [1]
<進程1> 購票成功
[進程2] 查看到剩余票數 [0]
沒有多余的票,<進程2> 購票失敗
[進程3] 查看到剩余票數 [0]
沒有多余的票,<進程3> 購票失敗
[進程4] 查看到剩余票數 [0]
沒有多余的票,<進程4> 購票失敗
發現使用join將並發改成穿行,確實能保證數據安全,但問題是連查票操作也變成只能一個一個人去查了,很明顯大家查票時應該是並發地去查詢而無需考慮數據准確與否,
此時join與互斥鎖的區別就顯而易見了,join是將一個任務整體串行,而互斥鎖的好處則是可以將一個任務中的某一段代碼串行,比如只讓task函數中的buy_ticket任務串行
def task(name,mutex):
query_ticket(name)
mutex.acquire()
buy_ticket(name)
mutex.release()
總結
加鎖可以保證多個進程修改同一塊數據時,同一時間只能有一個任務可以進行修改,即串行地修改,沒錯,速度是慢了,但犧牲了速度卻保證了數據安全。
雖然可以用文件共享數據實現進程間通信,但問題是:
1、效率低(共享數據基於文件,而文件是硬盤上的數據)
2、需要自己加鎖處理
因此我們最好找尋一種解決方案能夠兼顧:
1、效率高(多個進程共享一塊內存的數據)
2、幫我們處理好鎖問題。
這就是mutiprocessing模塊為我們提供的基於消息的IPC通信機制:隊列和管道。
隊列和管道都是將數據存放於內存中,而隊列又是基於(管道+鎖)實現的,可以讓我們從復雜的鎖問題中解脫出來,因而隊列才是進程間通信的最佳選擇。
