經過學習,咱們使用各類方法實現了程序的異步,讓多個任務能夠同時在幾個進程中併發處理,他們之間的運行沒有順序,一旦開啓也不受咱們控制。儘管併發編程讓咱們能更加充分的利用IO資源,可是也給咱們帶來了新的問題:當多個進程使用同一份數據資源的時候,就會引起數據安全或順序混亂問題。python
from multiprocessing import Process import os import time import random def work(n): print(f'{n,os.getpid()}is running') time.sleep(random.random()) print(f'{n,os.getpid()} is end') if __name__ == '__main__': for i in range(1,4): p = Process(target=work,args=(i,)) p.start()
(1, 4212)is running
(2, 8856)is running
(3, 5680)is running
(3, 5680) is end
(1, 4212) is end
(2, 8856) is end編程
from multiprocessing import Process,Lock import os import time import random def work(n,lock): lock.acquire() print(f'{n,os.getpid()}is running') time.sleep(random.random()) print(f'{n,os.getpid()} is end') lock.release() if __name__ == '__main__': lock = Lock() for i in range(1,4): p = Process(target=work,args=(i,lock)) p.start()
(1, 13304)is running
(1, 13304) is end
(2, 7524)is running
(2, 7524) is end
(3, 14552)is running
(3, 14552) is endjson
上面這種狀況雖然使用加鎖的形式實現了順序的執行,可是程序又從新變成串行了,這樣確實會浪費了時間,卻保證了數據的安全。小程序
咱們以模擬搶票爲例,來看看數據安全的重要性。安全
# 文件db的內容爲:{"count":1} # 注意必定要用雙引號,否則json沒法識別 # 併發運行,效率高,但競爭寫同一文件,數據寫入錯亂 from multiprocessing import Process,Lock import time,json,random def search(i): time.sleep(1) dic=json.load(open('ad.txt')) print('子進程%s查看剩餘票數%s' %(i,dic['count'])) def get(): dic=json.load(open('ad.txt')) time.sleep(1) # 模擬讀數據的網絡延遲 if dic['count'] >0: dic['count']-=1 time.sleep(0.2) # 模擬寫數據的網絡延遲 json.dump(dic,open('ad.txt','w')) print('購票成功') def task(i): search(i) get() if __name__ == '__main__': for i in range(1,15): # 模擬併發15個客戶端搶票 p=Process(target=task,args=(i,)) p.start()
子進程2查看剩餘票數1
子進程3查看剩餘票數1
子進程12查看剩餘票數1
子進程7查看剩餘票數1
子進程11查看剩餘票數1
子進程13查看剩餘票數1
子進程9查看剩餘票數1
子進程8查看剩餘票數1
子進程6查看剩餘票數1
子進程14查看剩餘票數1
子進程5查看剩餘票數1
子進程1查看剩餘票數1
子進程10查看剩餘票數1
子進程4查看剩餘票數1
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功
購票成功網絡
併發運行,效率高,但競爭寫同一文件,數據寫入錯亂併發
若是使用join方法的話,那這樣就是成了一個串行的概念了,這樣只能保證數據的安全性,可是程序的效率問題並無解決dom
from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db')) print('剩餘票數%s' %dic['count']) def get(): dic=json.load(open('db')) if dic['count'] >0: dic['count']-=1 time.sleep(0.2) # 模擬寫數據的網絡延遲 json.dump(dic,open('db','w')) print('購票成功') def task(): search() get() if __name__ == '__main__': for i in range(1,15): # 模擬併發15個客戶端搶票 p=Process(target=task) p.start() p.join()
子進程1查看剩餘票數1
購票成功
子進程2查看剩餘票數0
沒有票了
子進程3查看剩餘票數0
沒有票了
子進程4查看剩餘票數0
沒有票了
子進程5查看剩餘票數0
沒有票了
子進程6查看剩餘票數0
沒有票了
子進程7查看剩餘票數0
沒有票了
子進程8查看剩餘票數0
沒有票了
子進程9查看剩餘票數0
沒有票了
子進程10查看剩餘票數0
沒有票了
子進程11查看剩餘票數0
沒有票了
子進程12查看剩餘票數0
沒有票了
子進程13查看剩餘票數0
沒有票了
子進程14查看剩餘票數0
沒有票了異步
加鎖能夠保證多個進程修改同一塊數據時,同一時間只能有一個任務能夠進行修改,即串行的修改,沒錯,速度是慢了,但犧牲了速度卻保證了數據安全。學習
import json import time from multiprocessing import Process, Lock def check(): with open('db.txt','r',encoding='utf8') as fr: res = json.load(fr) return res def buy(i): with open('db.txt','r',encoding='utf8') as fr: res = json.load(fr) time.sleep(1) if res['count'] > 0: res['count'] -= 1 with open('db.txt','w',encoding='utf8') as fw: json.dump(res,fw) print(f'進程{i}搶票成功') time.sleep(0.5) # 模擬網絡io else: print(f'票已經售空!!!!') def test(i,lock): res = check() print(f'還剩{res["count"]}張票!') lock.acquire() # 至關於鎖頭。鎖住 buy(i) lock.release() # 釋放鎖頭 if __name__ == '__main__': lock = Lock() # 寫在這裏是爲了每個進程都拿到的是同一把鎖 for i in range(1,10):# 併發模擬10個子進程同時搶票 p = Process(target=test,args=(i,lock)) p.start() # 進程鎖,是把鎖住的代碼編程了串行 # join 是把全部的子進程編程了串行
還剩1張票! 還剩1張票! 還剩1張票! 還剩1張票! 還剩1張票! 還剩1張票! 還剩1張票! 還剩1張票! 還剩1張票! 進程5搶票成功 票已經售空!!!! 票已經售空!!!! 票已經售空!!!! 票已經售空!!!! 票已經售空!!!! 票已經售空!!!! 票已經售空!!!! 票已經售空!!!!