目錄python
經過以前的學習,咱們想方設法的實現了程序的異步,讓多個任務能夠同時在幾個進程中併發處理json
他們之間的運行沒有順序,一旦開啓也不受咱們的控制,隨之而來的帶來了新的問題:安全
當多個進程使用同一份數據資源的時候,就會引發數據安全或數據混亂問題服務器
# coding=utf-8 import os import time import random from multiprocessing import Process,Lock def work(n): print('%s: %s is running' %(n,os.getpid())) time.sleep(random.random()) print('%s:%s is done' %(n,os.getpid())) def run(n,lock): lock.acquire() work(n) lock.release() if __name__ == '__main__': lock = Lock() for i in range(3): p=Process(target=run,args=(i,lock)) p.start() 2: 6616 is running 2:6616 is done 0: 7052 is running 0:7052 is done 1: 10752 is running 1:10752 is done
上面狀況使用加鎖實現順序執行,程序由併發執行變成了串行執行。網絡
以模擬搶票軟件爲例,驗證數據的重要性數據結構
data 文件內容:{"count": 0} # coding=utf-8 from multiprocessing import Process,Lock import json import time # 查看餘票 def search(user): # 打開data文件查看餘票 with open("data","r",encoding="utf-8") as f: dic = json.load(f) print(f"{user} 查看餘票,還剩{dic.get('count')}") # 買票 def buy(user): # 先打開文件獲取車票數據 with open("data","r",encoding="utf-8") as f: dic = json.load(f) # 模擬網絡延遲 time.sleep(1) # 如有票修改data數據 if dic.get("count") > 0: dic["count"] -= 1 with open("data","w",encoding="utf-8") as f: json.dump(dic,f) print(f"{user} 搶到了車票!") else: print(f"{user} 搶票失敗") def run(user,lock): search(user) # 鎖住 lock.acquire() buy(user) # 釋放鎖 lock.release() # 給buy函數添加鎖,讓併發變成了串行 # 犧牲了執行效率,但保證了數據的安全 # 在程序併發執行時,須要修改數據時使用互斥鎖 if __name__ == '__main__': # 建立一把鎖 lock = Lock() # 模擬併發10個客戶端搶票 for i in range(10): p = Process(target=run,args=(f"服務器 {i} ",lock)) p.start() 服務器 2 查看餘票,還剩1 服務器 3 查看餘票,還剩1 服務器 7 查看餘票,還剩1 服務器 6 查看餘票,還剩1 服務器 2 搶到了車票! 服務器 0 查看餘票,還剩0 服務器 8 查看餘票,還剩0 服務器 4 查看餘票,還剩0 服務器 1 查看餘票,還剩0 服務器 9 查看餘票,還剩0 服務器 5 查看餘票,還剩0 服務器 3 搶票失敗 服務器 7 搶票失敗 服務器 6 搶票失敗 服務器 0 搶票失敗 服務器 8 搶票失敗 服務器 4 搶票失敗 服務器 1 搶票失敗 服務器 9 搶票失敗 服務器 5 搶票失敗
加鎖能夠保證多個進程在修改同一塊數據的時候,同一時間只能有一個任務能夠進行修改,即串行的修改,這樣速度是慢了,可是保證了數據的安全。併發
至關於在內存中產生一個隊列空間,能夠存放多個數據,遵循先進先出原則dom
# coding=utf-8 from multiprocessing import Queue ''' Queue 用法: ''' # 建立出q對象,Queue類能夠傳參數(), # 5表示能夠存5個值 # 若不填能夠存無限個值 q = Queue(5) # 添加數據 q.put("1") q.put("2") q.put("3") q.put("4") q.put("5") # 查看隊列中是否滿了 True print(q.full()) # 繼續添加數據,若隊列滿了就報錯 queue.Full q.put_nowait("6") # 獲取數據,遵循先進先出,,若隊列中無數據可取,也會卡主 print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 判斷隊列是否爲空 True print(q.empty()) # 繼續獲取數據,若隊列中沒有數據了就報錯 queue.Empty print(q.get_nowait())
堆棧是一個後進先出的數據結構,工做方式就像一對汽車排隊進去一個死衚衕裏面,最早進去的必定是最後出來異步
進程間數據時相互隔離的,若想實現進程間通訊,能夠利用隊列。函數
# coding=utf-8 from multiprocessing import Process from multiprocessing import Queue def test1(q): data = "數據" # 添加隊列數據 q.put(data) print("子進程1開始添加數據到隊列中") def test2(q): # 獲取隊列數據 data = q.get() print(f"子進程2從隊列中獲取數據:{data}") if __name__ == '__main__': q = Queue() p1 = Process(target=test1,args=(q,)) p2 = Process(target=test2,args=(q,)) p1.start() p2.start() print("主程序")
生產者:生產數據的
消費者:使用數據的
生活中:好比賣油條,一邊生產油條,一邊買油條,供需不平衡
程序中:經過隊列,生產者把數據添加隊列中,消費者從隊列中獲取數據
# coding=utf-8 from multiprocessing import Process,Queue import time # 生產者 def producer(name,food,q): for i in range(10): data = food,i print(f"用戶{name} 製做了{data}") q.put(data) time.sleep(0.1) # 消費者 def consumer(name,q): while True: # time.sleep(1) data = q.get() if not data: break print(f"用戶{name} 開始吃{data}") if __name__ == '__main__': q = Queue() # 創造生產者 p1 = Process(target=producer,args=("qinyj","油條",q)) # 創造消費者 c1 = Process(target=consumer,args=("qinyj",q)) p1.start() # 添加消費者守護進程,表示跟着主程序一塊兒停掉,即生產者不生產了,消費者也不吃了 c1.daemon = True c1.start() # 添加join方法,生產者生產完畢以後就停掉,而後消費者也會一直吃 p1.join() # time.sleep(10) # 添加延遲,等待生產者生產完畢把全部進程死掉 print("主程序") 用戶qinyj 製做了('油條', 0) 用戶qinyj 開始吃('油條', 0) 用戶qinyj 製做了('油條', 1) 用戶qinyj 開始吃('油條', 1) 用戶qinyj 製做了('油條', 2) 用戶qinyj 開始吃('油條', 2) 用戶qinyj 製做了('油條', 3) 用戶qinyj 開始吃('油條', 3) 用戶qinyj 製做了('油條', 4) 用戶qinyj 開始吃('油條', 4) 用戶qinyj 製做了('油條', 5) 用戶qinyj 開始吃('油條', 5) 用戶qinyj 製做了('油條', 6) 用戶qinyj 開始吃('油條', 6) 用戶qinyj 製做了('油條', 7) 用戶qinyj 開始吃('油條', 7) 用戶qinyj 製做了('油條', 8) 用戶qinyj 開始吃('油條', 8) 用戶qinyj 製做了('油條', 9) 用戶qinyj 開始吃('油條', 9) 主程序