一 行程物件及其他方法
'''
一臺計算機上面運行著很多行程,那么計算機是如何區分并管理這些行程服務端的呢?
計算機會給每一個運行的行程分配一個PID號
如何查看
windows電腦
進入cmd輸入tasklist即可查看
tasklist|findstr PID查看具體的行程
linux電腦
進入終端之后輸入ps aux
ps aux|grep PID查看具體的行程
'''
from multiprocessing import Process, current_process import time import os def task(): print('{} is running'.format(current_process().pid)) print('{} is running'.format(os.getpid())) print('{} 是子行程的主行程號'.format(os.getppid())) time.sleep(5) if __name__ == '__main__': p = Process(target=task) p.start() # p.terminate() # 殺死當前行程 # 是告訴作業系統幫你去殺死當前行程 但是需要一定的時間 而代碼的運行速度極快,加上time.sleep,列印結果為True # time.sleep(0.001) print(p.is_alive()) # 判斷當前行程是否存活 ''' 一般情況下我們會默認將 存盤布林值的變數名 和回傳的結果是布林值的方法名 都起成以is_開頭 ''' print('主', current_process().pid) print('主行程的父行程號', os.getpid()) ''' rue 主 6308 主行程的父行程號 6308 16144 is running 16144 is running 6308 是子行程的主行程號 '''
current_process().pid和os.getpid()都是獲取當前行程的行程號,
os.getppid()獲取當前行程的父行程
p.terminate() 殺死當前行程
p.is_alive()判斷當前行程是否存活
二 僵尸行程與孤兒行程(了解)
1、僵尸行程
死了但是沒有死透
當你開設了子行程之后,該行程死后不會立刻釋放占用的行程號
因為我要讓父行程能夠查看到它開設的子行程的一些基本資訊 占用的pid號 運行時間
所有的行程都會步入僵尸行程
父行程不死并且在無限制的創建子行程并且子行程也不結束
回收子行程占用的pid號
父行程等待子行程運行結束
父行程呼叫join方法
2、孤兒行程
子行程存活,父行程意味死亡
作業系統會開設一個“兒童福利院”專門管理孤兒行程回收相關資源
三 守護行程
會隨著主行程的結束而結束,
主行程創建守護行程
其一:守護行程會在主行程代碼執行結束后就終止
其二:守護行程內無法再開啟子行程,否則拋出例外:AssertionError: daemonic processes are not allowed to have children
注意:行程之間是互相獨立的,主行程代碼運行結束,守護行程隨即終止
from multiprocessing import Process import time def task(name): print('{}正在活著'.format(name)) time.sleep(3) print('{}正在死亡'.format(name)) if __name__ == '__main__': p = Process(target=task, args=('xiaobao',)) p.daemon = True # 將行程p設定成守護行程 這一句一定要放在start方法上面才有效否則會直接報錯 p.start() print('haha') ''' haha '''
四 互斥鎖
多個行程操作同一份資料的時候,會出現資料錯亂的問題
針對上述問題,解決方式就是加鎖處理
將并發變成串行,犧牲效率但是保證了資料的安全
from multiprocessing import Process, Lock import json import time import random # 查票 def search(i): # 檔案操作讀取票數 with open('data', 'r', encoding='utf-8') as f: dic = json.load(f) # 操作檔案用load,具體物件資料用loads print('用戶名{}查詢余票{}'.format(i, dic.get('ticket_num'))) # 字典取值不要用[]的形式,如果沒有,會報錯,推薦使用get,寫代碼盡量避免報錯, # 買票 1.先查 2.再買 def buy(i): # 先查票 with open('data', 'r', encoding='utf-8') as f: dic = json.load(f) # 模擬網路延遲 time.sleep(random.randint(1, 3)) # 判斷當前是否有票 if dic.get('ticket_num') > 0: # 修改資料庫 買票 dic['ticket_num'] -= 1 # 寫入資料庫 with open('data', 'w', encoding='utf-8') as f: json.dump(dic, f) print('用戶{}買票成功'.format(i)) else: print('用戶{}買票失敗'.format(i)) # 整合上面兩個函式 def run(i, mutex): search(i) # 給買票環節加鎖處理 # 搶鎖 mutex.acquire() buy(i) # 釋放鎖 mutex.release() if __name__ == '__main__': # 在主行程中生成一把鎖 讓所有的子行程搶 誰先搶到誰先買票 mutex = Lock() for i in range(1, 11): p = Process(target=run, args=(i, mutex)) p.start()
模擬搶票,一個用戶對應一個行程,余票剩余1張,不加鎖的情況下,因是并發,10個用戶
顯示都夠買成功了1張,實際情況只能有1個用戶購買成功,所以加鎖,10個用戶搶鎖
,鎖先搶到,誰先運行,讓并發效果變成了串行效果,犧牲了效率,保證了資料安全
'''
擴展 行鎖 表鎖
注意:
1、鎖不要輕易的使用,容易造成死鎖現象(我們寫代碼一般不會用到,都是內部封裝好的)
2、鎖只在處理資料的部分來保證資料安全(只在爭搶資料的環節加鎖處理即可)
'''
五 行程間通信
佇列:先進先出(扶手電梯)
堆疊:先進后出(箱子里疊衣服)
管道:subprocess
stdin stdout stderr
佇列:管道+鎖
from multiprocessing import Queue # 創建一個佇列 q = Queue(5) # 括號內可以傳數字 表示生成的佇列最大可以同時存放的資料量 q.put(111) q.put(222) q.put(333) print(q.full()) # 判斷當前佇列是否滿了 print(q.empty()) # 判斷當前佇列是否空了 q.put(444) q.put(555) # q.put(666) # 當佇列資料放滿了之后 如果還有資料要放程式會阻塞 直到有位置讓出來 ''' 存取資料 存是為了更好的取 千方百計的存、簡單快捷的取 ''' # 去佇列中取資料 v1 = q.get() v2 = q.get() v3 = q.get() v4 = q.get() v5 = q.get() # v6 = q.get() # 佇列中如果已經沒有資料的話 get方法會原地阻塞 try: v6 = q.get(timeout=3) print(v6) except Exception as e: print('已經被取完了') print(v1) ''' q.full() q.empty() q.get_nowait() 在多行程的情況下是不精準 '''
put 當佇列資料放滿了之后 如果還有資料要放程式會阻塞 直到有位置讓出來
get 佇列中如果已經沒有資料的話 get方法會原地阻塞
IPC機制
from multiprocessing import Process, Queue ''' 研究思路 1、主行程跟子行程借助于佇列通信 2、子行程跟子行程借助于佇列通信 ''' def producer(q): q.put('zd') print('hello llx') def consumer(q): print(q.get()) if __name__ == '__main__': q = Queue() p = Process(target=producer, args=(q,)) p1 = Process(target=consumer, args=(q,)) p.start() p1.start() ''' hello llx zd '''
六 生產者消費者模型
生產者:生產/制造東西的
消費者:消費/處理東西的
該模型除了上述兩個之外還需要一個媒介
生產者和消費者之間不是直接做互動的,而是借助于媒介做互動
生產者+消費佇列+消費者
生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題,
生產者和消費者彼此之間不直接通訊,而通過阻塞佇列來進行通訊,
所以生產者生產完資料之后不用等待消費者處理,直接扔給阻塞佇列,
消費者不找生產者要資料,而是直接從阻塞佇列里取,阻塞佇列就相當于一個緩沖區,
平衡了生產者和消費者的處理能力,
from multiprocessing import Process, Queue import time import random def producer(name, food, q): for i in range(10): data = '{}生產了{}{}'.format(name, i, food) # 模擬延遲 time.sleep(random.randint(1, 3)) print(data) # 將資料放入佇列中 q.put(data) def consumer(name, q): # 消費者胃口很大 光碟行動 while True: food = q.get() if food is None: break time.sleep(random.randint(1, 3)) print('{}吃了{}'.format(name, food)) if __name__ == '__main__': q = Queue() p1 = Process(target=producer, args=('大廚lq', '包子', q)) p2 = Process(target=producer, args=('大廚zd', '油條', q)) c1 = Process(target=consumer, args=('吃貨xiao', q)) c2 = Process(target=consumer, args=('吃貨xiaoxiaobao', q)) p1.start() p2.start() c1.start() c2.start() p1.join() p2.join() # 等待生產者生產完畢之后 往佇列中添加特定的結束符號 q.put(None) # 肯定在所有生產者生產的資料的末尾,有幾個消費者,就要有幾個put(None) q.put(None) # 主行程與子行程的通信
解決生產者生產完畢后,有幾個消費者就需要向幾個消費者添加幾個put(None)的結束標志的問題,
使用JoinableQueue模塊
from multiprocessing import Process, JoinableQueue import time import random def producer(name, food, q): for i in range(10): data = '{}生產了{}{}'.format(name, i, food) # 模擬延遲 time.sleep(random.randint(1, 3)) print(data) # 將資料放入佇列中 q.put(data) def consumer(name, q): # 消費者胃口很大 光碟行動 while True: food = q.get() time.sleep(random.randint(1, 3)) print('{}吃了{}'.format(name, food)) q.task_done() # 告訴佇列你已經從里面取出了一個資料并且處理完畢了 if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=producer, args=('大廚lq', '包子', q)) p2 = Process(target=producer, args=('大廚zd', '油條', q)) c1 = Process(target=consumer, args=('吃貨xiao', q)) c2 = Process(target=consumer, args=('吃貨cyz', q)) p1.start() p2.start() # 將消費者設定成守護行程 c1.daemon = True c2.daemon = True c1.start() c2.start() p1.join() p2.join() q.join() # 等待佇列中所有的資料被取完再執行往下執行代碼 ''' JoinableQueue 每當你往佇列中存入資料的時候 內部會有一個計數器+1 每當你呼叫task_done的時候 計數器-1 q.join() 當計數器為0的時候 才往后運行 #主行程等--->p1,p2等---->c1,c2 #p1,p2結束了,證明c1,c2肯定全都收完了p1,p2發到佇列的資料 #因而c1,c2也沒有存在的價值了,不需要繼續阻塞在行程中影響主行程了,應該隨著主行程的結束而結束,所以設定成守護行程就可以了, '''
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552314.html
標籤:其他
上一篇:使用 IDEA 時突然斷電導致 git 本地分支損壞的解決方案
下一篇:返回列表
