進程之間數據不共享,可是共享同一套文件系統,因此訪問同一個文件,或同一個打印終端,是沒有問題的,而共享帶來的是競爭,競爭帶來的結果就是錯亂,以下:python
#併發運行,效率高,但競爭同一打印終端,帶來了打印錯亂 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()
如何控制,就是加鎖處理。互斥鎖的意思就是互相排斥,若是把多個進程比喻爲多我的,互斥鎖的工做原理就是多我的都要去爭搶同一個資源:衛生間。一我的搶到衛生間後上一把鎖,其它人都要等着,等到這個任務完成後釋放鎖,其餘人才有可能有一個搶到。因此互斥鎖的原理,就是把併發改爲串行,下降了效率,但保證了數據俺去那不錯亂。數據庫
#由併發變成了串行,犧牲了運行效率,但避免了競爭 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()
多個進程共享同一個文件,咱們能夠把文件當數據庫,用多個進程模擬多我的執行搶票任務。編程
#文件db.txt的內容爲:{"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 查到剩餘票數%s\033[0m' %(name,dic['count'])) def get(name): dic=json.load(open('db.txt')) time.sleep(1) #模擬讀數據的網絡延遲 if dic['count'] >0: dic['count']-=1 time.sleep(1) #模擬寫數據的網絡延遲 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): #模擬併發10個客戶端搶票 name='<路人%s>' %i p=Process(target=task,args=(name,)) p.start()
併發運行,效率高,但競爭寫同一文件,數據寫入錯亂,只有一張票,賣成功給了10我的。json
<路人0> 查到剩餘票數1 <路人1> 查到剩餘票數1 <路人2> 查到剩餘票數1 <路人3> 查到剩餘票數1 <路人4> 查到剩餘票數1 <路人5> 查到剩餘票數1 <路人6> 查到剩餘票數1 <路人7> 查到剩餘票數1 <路人8> 查到剩餘票數1 <路人9> 查到剩餘票數1 <路人0> 購票成功 <路人4> 購票成功 <路人1> 購票成功 <路人5> 購票成功 <路人3> 購票成功 <路人7> 購票成功 <路人2> 購票成功 <路人6> 購票成功 <路人8> 購票成功 <路人9> 購票成功
加鎖處理:購票行爲由併發變成了串行,犧牲了運行效率,但保證了數據安全。安全
#把文件db.txt的內容重置爲:{"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 查到剩餘票數%s\033[0m' %(name,dic['count'])) def get(name): dic=json.load(open('db.txt')) time.sleep(1) #模擬讀數據的網絡延遲 if dic['count'] >0: dic['count']-=1 time.sleep(1) #模擬寫數據的網絡延遲 json.dump(dic,open('db.txt','w')) print('\033[46m%s 購票成功\033[0m' %name) def task(name,lock): search(name) with lock: #至關於lock.acquire(),執行完自代碼塊自動執行lock.release() get(name) if __name__ == '__main__': lock=Lock() for i in range(10): #模擬併發10個客戶端搶票 name='<路人%s>' %i p=Process(target=task,args=(name,lock)) p.start()
執行結果:網絡
<路人0> 查到剩餘票數1 <路人1> 查到剩餘票數1 <路人2> 查到剩餘票數1 <路人3> 查到剩餘票數1 <路人4> 查到剩餘票數1 <路人5> 查到剩餘票數1 <路人6> 查到剩餘票數1 <路人7> 查到剩餘票數1 <路人8> 查到剩餘票數1 <路人9> 查到剩餘票數1 <路人0> 購票成功
使用join能夠將併發變成串行,互斥鎖的原理也是將併發變成串行,那咱們直接使用join就能夠了,爲什麼還要用互斥鎖?併發
把文件db.txt的內容重置爲:{「count」:1}函數
from multiprocessing import Process,Lock import time,json def search(name): dic=json.load(open('db.txt')) print('\033[43m%s 查到剩餘票數%s\033[0m' %(name,dic['count'])) def get(name): dic=json.load(open('db.txt')) time.sleep(1) #模擬讀數據的網絡延遲 if dic['count'] >0: dic['count']-=1 time.sleep(1) #模擬寫數據的網絡延遲 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()
執行結果:ui
<路人0> 查到剩餘票數1 <路人0> 購票成功 <路人1> 查到剩餘票數0 <路人2> 查到剩餘票數0 <路人3> 查到剩餘票數0 <路人4> 查到剩餘票數0 <路人5> 查到剩餘票數0 <路人6> 查到剩餘票數0 <路人7> 查到剩餘票數0 <路人8> 查到剩餘票數0 <路人9> 查到剩餘票數0
發現使用join將併發改爲穿行,確實能保證數據安全,但問題是連查票操做也變成只能一個一我的去查了,很明顯你們查票時應該是併發地去查詢而無需考慮數據準確與否,此時join與互斥鎖的區別就顯而易見了,join是將一個任務總體串行,而互斥鎖的好處則是能夠將一個任務中的某一段代碼串行,好比只讓task函數中的get任務串行。code
def task(name,): search(name) # 併發執行 lock.acquire() get(name) lock.release()
加鎖能夠保證多個進程修改同一塊數據時,同一時間只能有一個任務能夠進行修改,即串行地修改,沒有,速度是慢了,但犧牲了速度卻保證了數據安全。
雖然能夠用文件共享數據實現進程間通訊,但問題是:
一、效率低(共享數據基於文件,而文件是硬盤上的數據)
二、須要本身加鎖處理
所以咱們最好尋找一種解決方案可以兼顧:
一、效率高(多個進程共享一塊內存的數據)
二、幫咱們處理好鎖問題
這就是multiprocessing模塊爲咱們提供的基於消息的IPC通訊機制:隊列和管道。
隊列和管道都是將數據存放於內存中,而隊列又是基於[管道+鎖]實現的,可讓咱們從複雜的鎖問題中解脫出來,於是隊列纔是進程間通訊的最佳選擇。
咱們應該儘可能避免使用共享數據,儘量使用消息傳遞和隊列,避免處理複雜的同步和鎖問題,並且在進程數目增多時,每每能夠得到更好的可擴展性。