一 互斥鎖
進程之間數(shù)據(jù)不共享,但是共享同一套文件系統(tǒng),所以訪問同一個文件,或同一個打印終端,是沒有問題的,而共享帶來的是競爭,競爭帶來的結(jié)果就是錯亂苍碟,如下
并發(fā)運行,效率高,但競爭同一打印終端,帶來了打印錯亂
from multiprocessing import Process
import os,time
def work():
print('%s is running' %os.getpid())
time.sleep(2)
print('%s is done' %os.getpid())
if name == 'main':
for i in range(3):
p=Process(target=work)
p.start()
如何控制牺蹄,就是加鎖處理忘伞。而互斥鎖的意思就是互相排斥,如果把多個進程比喻為多個人沙兰,互斥鎖的工作原理就是多個人都要去爭搶同一個資源:衛(wèi)生間氓奈,一個人搶到衛(wèi)生間后上一把鎖,其他人都要等著鼎天,等到這個完成任務(wù)后釋放鎖舀奶,其他人才有可能有一個搶到......所以互斥鎖的原理,就是把并發(fā)改成穿行斋射,降低了效率伪节,但保證了數(shù)據(jù)安全不錯亂
由并發(fā)變成了串行,犧牲了運行效率,但避免了競爭
from multiprocessing import Process,Lock
import os,time
def work(lock):
lock.acquire() #加鎖
print('%s is running' %os.getpid())
time.sleep(2)
print('%s is done' %os.getpid())
lock.release() #釋放鎖
if name == 'main':
lock=Lock()
for i in range(3):
p=Process(target=work,args=(lock,))
p.start()
二 模擬搶票練習(xí)
多個進程共享同一文件,我們可以把文件當(dāng)數(shù)據(jù)庫绩鸣,用多個進程模擬多個人執(zhí)行搶票任務(wù)
文件db.txt的內(nèi)容為:{"count":1}
注意一定要用雙引號怀大,不然json無法識別
from multiprocessing import Process
import time,json
def search(name):
dic=json.load(open('db.txt'))
time.sleep(1)
print('\033[43m%s 查到剩余票數(shù)%s\033[0m' %(name,dic['count']))
def get(name):
dic=json.load(open('db.txt'))
time.sleep(1) #模擬讀數(shù)據(jù)的網(wǎng)絡(luò)延遲
if dic['count'] >0:
dic['count']-=1
time.sleep(1) #模擬寫數(shù)據(jù)的網(wǎng)絡(luò)延遲
json.dump(dic,open('db.txt','w'))
print('\033[46m%s 購票成功\033[0m' %name)
def task(name):
search(name)
get(name)
if name == 'main':
for i in range(10): #模擬并發(fā)10個客戶端搶票
name='<路人%s>' %i
p=Process(target=task,args=(name,))
p.start()
并發(fā)運行,效率高呀闻,但競爭寫同一文件化借,數(shù)據(jù)寫入錯亂,只有一張票,賣成功給了10個人
<路人0> 查到剩余票數(shù)1
<路人1> 查到剩余票數(shù)1
<路人2> 查到剩余票數(shù)1
<路人3> 查到剩余票數(shù)1
<路人4> 查到剩余票數(shù)1
<路人5> 查到剩余票數(shù)1
<路人6> 查到剩余票數(shù)1
<路人7> 查到剩余票數(shù)1
<路人8> 查到剩余票數(shù)1
<路人9> 查到剩余票數(shù)1
<路人0> 購票成功
<路人4> 購票成功
<路人1> 購票成功
<路人5> 購票成功
<路人3> 購票成功
<路人7> 購票成功
<路人2> 購票成功
<路人6> 購票成功
<路人8> 購票成功
<路人9> 購票成功
加鎖處理:購票行為由并發(fā)變成了串行捡多,犧牲了運行效率蓖康,但保證了數(shù)據(jù)安全
把文件db.txt的內(nèi)容重置為:{"count":1}
from multiprocessing import Process,Lock
import time,json
def search(name):
dic=json.load(open('db.txt'))
time.sleep(1)
print('\033[43m%s 查到剩余票數(shù)%s\033[0m' %(name,dic['count']))
def get(name):
dic=json.load(open('db.txt'))
time.sleep(1) #模擬讀數(shù)據(jù)的網(wǎng)絡(luò)延遲
if dic['count'] >0:
dic['count']-=1
time.sleep(1) #模擬寫數(shù)據(jù)的網(wǎng)絡(luò)延遲
json.dump(dic,open('db.txt','w'))
print('\033[46m%s 購票成功\033[0m' %name)
def task(name,lock):
search(name)
with lock: #相當(dāng)于lock.acquire(),執(zhí)行完自代碼塊自動執(zhí)行l(wèi)ock.release()
get(name)
if name == 'main':
lock=Lock()
for i in range(10): #模擬并發(fā)10個客戶端搶票
name='<路人%s>' %i
p=Process(target=task,args=(name,lock))
p.start()
執(zhí)行結(jié)果
<路人0> 查到剩余票數(shù)1
<路人1> 查到剩余票數(shù)1
<路人2> 查到剩余票數(shù)1
<路人3> 查到剩余票數(shù)1
<路人4> 查到剩余票數(shù)1
<路人5> 查到剩余票數(shù)1
<路人6> 查到剩余票數(shù)1
<路人7> 查到剩余票數(shù)1
<路人8> 查到剩余票數(shù)1
<路人9> 查到剩余票數(shù)1
<路人0> 購票成功
三 互斥鎖與join
使用join可以將并發(fā)變成串行,互斥鎖的原理也是將并發(fā)變成穿行垒手,那我們直接使用join就可以了啊蒜焊,為何還要互斥鎖,說到這里我趕緊試了一下
把文件db.txt的內(nèi)容重置為:{"count":1}
from multiprocessing import Process,Lock
import time,json
def search(name):
dic=json.load(open('db.txt'))
print('\033[43m%s 查到剩余票數(shù)%s\033[0m' %(name,dic['count']))
def get(name):
dic=json.load(open('db.txt'))
time.sleep(1) #模擬讀數(shù)據(jù)的網(wǎng)絡(luò)延遲
if dic['count'] >0:
dic['count']-=1
time.sleep(1) #模擬寫數(shù)據(jù)的網(wǎng)絡(luò)延遲
json.dump(dic,open('db.txt','w'))
print('\033[46m%s 購票成功\033[0m' %name)
def task(name,):
search(name)
get(name)
if name == 'main':
for i in range(10):
name='<路人%s>' %i
p=Process(target=task,args=(name,))
p.start()
p.join()
執(zhí)行結(jié)果
<路人0> 查到剩余票數(shù)1
<路人0> 購票成功
<路人1> 查到剩余票數(shù)0
<路人2> 查到剩余票數(shù)0
<路人3> 查到剩余票數(shù)0
<路人4> 查到剩余票數(shù)0
<路人5> 查到剩余票數(shù)0
<路人6> 查到剩余票數(shù)0
<路人7> 查到剩余票數(shù)0
<路人8> 查到剩余票數(shù)0
<路人9> 查到剩余票數(shù)0
發(fā)現(xiàn)使用join將并發(fā)改成穿行科贬,確實能保證數(shù)據(jù)安全泳梆,但問題是連查票操作也變成只能一個一個人去查了,很明顯大家查票時應(yīng)該是并發(fā)地去查詢而無需考慮數(shù)據(jù)準(zhǔn)確與否榜掌,此時join與互斥鎖的區(qū)別就顯而易見了优妙,join是將一個任務(wù)整體串行,而互斥鎖的好處則是可以將一個任務(wù)中的某一段代碼串行憎账,比如只讓task函數(shù)中的get任務(wù)串行
def task(name,):
search(name) # 并發(fā)執(zhí)行
lock.acquire()
get(name) #串行執(zhí)行
lock.release()
四 總結(jié)
加鎖可以保證多個進程修改同一塊數(shù)據(jù)時套硼,同一時間只能有一個任務(wù)可以進行修改,即串行地修改胞皱,沒錯邪意,速度是慢了九妈,但犧牲了速度卻保證了數(shù)據(jù)安全。
雖然可以用文件共享數(shù)據(jù)實現(xiàn)進程間通信雾鬼,但問題是:
1萌朱、效率低(共享數(shù)據(jù)基于文件,而文件是硬盤上的數(shù)據(jù))
2呆贿、需要自己加鎖處理
因此我們最好找尋一種解決方案能夠兼顧:
1、效率高(多個進程共享一塊內(nèi)存的數(shù)據(jù))
2森渐、幫我們處理好鎖問題做入。
這就是mutiprocessing模塊為我們提供的基于消息的IPC通信機制:隊列和管道神汹。
隊列和管道都是將數(shù)據(jù)存放于內(nèi)存中社付,而隊列又是基于(管道+鎖)實現(xiàn)的蠕趁,可以讓我們從復(fù)雜的鎖問題中解脫出來锣光,因而隊列才是進程間通信的最佳選擇粗恢。
我們應(yīng)該盡量避免使用共享數(shù)據(jù)另锋,盡可能使用消息傳遞和隊列评架,避免處理復(fù)雜的同步和鎖問題丁眼,而且在進程數(shù)目增多時埠况,往往可以獲得更好的可獲展性耸携。