python全棧開發基礎【第二十一篇】互斥鎖以及進程之間的三種通信方式(IPC)以及生產者個消費者模型
一、互斥鎖
進程之間數據隔離,但是共享一套文件系統,因而可以通過文件來實現進程直接的通信,但問題是必須自己加鎖處理。
註意:加鎖的目的是為了保證多個進程修改同一塊數據時,同一時間只能有一個修改,即串行的修改,沒錯,速度是慢了,犧牲了速度而保證了數據安全。
1.上廁所的小例子:你上廁所的時候肯定得鎖門吧,有人來了看見門鎖著,就會在外面等著,等你吧門開開出來的時候,下一個人才去上廁所。
from multiprocessing import Process,Lock import os import time def work(mutex): mutex.acquire() print(‘task[%s] 上廁所‘%os.getpid()) time.sleep(3) print(‘task[%s] 上完廁所‘%os.getpid()) mutex.release() if __name__ == ‘__main__‘: mutex = Lock() p1 = Process(target=work,args=(mutex,)) p2 = Process(target=work,args=(mutex,)) p3 = Process(target=work,args=(mutex,)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print(‘主‘)
2.模擬搶票(也是利用了互斥鎖的原理 :LOCK互斥鎖)
import json import time import random import os from multiprocessing import Process,Lock def chakan(): dic = json.load(open(‘piao‘,)) # 先查看票數,也就是打開那個文件 print(‘剩余票數:%s‘ % dic[‘count‘]) # 查看剩余的票數 def buy(): dic = json.load(open(‘piao‘,)) if dic[‘count‘]>0: #如果還有票 dic[‘count‘]-=1 #就修改裏面的值-1 time.sleep(random.randint(1,3)) #執行裏面買票的一系列操作就先不執行了,讓睡一會代替(並且隨機的睡) json.dump(dic,open(‘piao‘,‘w‘)) print(‘%s 購票成功‘ % os.getpid()) # 當前的那個id購票成功 def task(mutex): #搶票 chakan() #因為查看的時候大家都可以看到,不需要加鎖 mutex.acquire() #加鎖 buy() #買的時候必須一個一個的買,先等一個人買完了,後面的人在買 mutex.release() #取消鎖 if __name__ == ‘__main__‘: mutex = Lock() for i in range(50):#讓50個人去訪問那個票數 p = Process(target=task,args=(mutex,)) p.start()
二、Process對象的其他屬性
p.daemon :守護進程(必須在開啟之前設置守護進程):如果父進程死,子進程p也死了
p.join:父進程等p執行完了才運行主進程,是父進程阻塞在原地,而p仍然在後臺運行。
terminate:強制關閉。(確保p裏面沒有其他子進程的時候關閉,如果裏面有子進程,你去用這個方法強制關閉了就會產生僵屍進程(打個比方:如果你老子掛了,你還沒掛,那麽就沒人給你收屍了,啊哈哈))
is_alive:關閉進程的時候,不會立即關閉,所以is_alive立刻查看的結果可能還是存活
p.join():父進程在等p的結束,是父進程阻塞在原地,而p仍然在後臺運行
p.name:查看名字
p.pid :查看id
我們可以簡單介紹一下僵屍進程:
子進程運行完成,但是父進程遲遲沒有進行回收,此時子進程實際上並沒有退出,其仍然占用著系統資源,這樣的?進程稱為僵屍進程。
因為僵屍進程的資源一直未被回收,造成了系統資源的浪費,過多的僵屍進程將造成系統性能下降,所以應避免出現僵屍進程。
from multiprocessing import Process import os import time def work(): print(‘%s is working‘%os.getpid()) time.sleep(3) if __name__ == ‘__main__‘: p1 =Process(target=work) p2 =Process(target=work) p3 =Process(target=work) # p1.daemon = True # p2.daemon = True #守護進程(守護他爹) # p3.daemon = True #主進程死了子進程也死了(就不會執行子進程了) p1.start() p2.start() p3.start() p3.join() p2.join() p1.join() #多個join就是在等花費時間最長的那個運行完就執行主程序了 print(‘主程序‘) # -了解方法--------------- # p1.terminate() #強制關閉進程 # time.sleep(3) # print(p1.is_alive()) #看是不是還活著 # print(p1.name) #查看進程名字 # print(p1.pid) #查看id號 # print(‘主程序‘)
三、進程間的三種通信(IPC)方式:
方式一:隊列(推薦使用)
進程彼此之間互相隔離,要實現進程間通信(IPC),multiprocessing模塊支持兩種形式:隊列和管道,這兩種方式都是使用消息傳遞的
1.隊列:隊列類似於一條管道,元素先進先出
需要註意的一點是:隊列都是在內存中操作,進程退出,隊列清空,另外,隊列也是一個阻塞的形態
2.隊列分類
隊列有很多種,但都依賴與模塊queue
queue.Queue() #先進先出
queue.LifoQueue() #後進先出
queue.PriorityQueue() #優先級隊列
queue.deque() #雙線隊列
創建隊列的類(底層就是以管道和鎖定的方式實現):
Queue([maxsize]):創建共享的進程隊列,Queue是多進程安全的隊列,
可以使用Queue實現多進程之間的數據傳遞。
參數介紹:
maxsize是隊列中允許最大項數,省略則無大小限制。
方法介紹:
q.put方法用以插入數據到隊列中,put方法還有兩個可選參數:blocked和timeout。如果blocked為True(默認值),並且timeout為正值,該方法會阻塞timeout指定的時間,直到該隊列有剩余的空間。如果超時,會拋出Queue.Full異常。如果blocked為False,但該Queue已滿,會立即拋出Queue.Full異常。 q.get方法可以從隊列讀取並且刪除一個元素。同樣,get方法有兩個可選參數:blocked和timeout。如果blocked為True(默認值),並且timeout為正值,那麽在等待時間內沒有取到任何元素,會拋出Queue.Empty異常。如果blocked為False,有兩種情況存在,如果Queue有一個值可用,則立即返回該值,否則,如果隊列為空,則立即拋出Queue.Empty異常. q.get_nowait():同q.get(False) q.put_nowait():同q.put(False) q.empty():調用此方法時q為空則返回True,該結果不可靠,比如在返回True的過程中,如果隊列中又加入了項目。 q.full():調用此方法時q已滿則返回True,該結果不可靠,比如在返回True的過程中,如果隊列中的項目被取走。 q.qsize():返回隊列中目前項目的正確數量,結果也不可靠,理由同q.empty()和q.full()一樣
應用:
#隊列 # 1.可以往隊列裏放任意類型的 # 2.先進先出 from multiprocessing import Process,Queue q= Queue(3) q.put(‘first‘) #默認block=True q.put(‘second‘) q.put(‘third‘) print(q.get()) print(q.get()) print(q.get())
python全棧開發基礎【第二十一篇】互斥鎖以及進程之間的三種通信方式(IPC)以及生產者個消費者模型