建立程序,join方法,程序物件相關屬性和方法,殭屍程序和孤兒程序,守護程序,互斥鎖
阿新 • • 發佈:2022-04-20
建立程序
在python中提供了一個multiprocessing模組可以幫助我們使用多程序解決問題。在multiprocessing
模組中有一個類Process。
from multiprocessing import Process ''' group=None, 為日後開發新功能準備 target=None, 目標任務 name=None, 程序的姓名 args=(), 目標任務需要的位置引數 kwargs={}, 目標任務需要的字典引數 daemon=None):預設值為False,如果設為True,代表p為後臺執行的守護程序,當p的父程序終止時,p也隨之終止,並且設定為True後,p不能建立自己的新程序,必須在p.start()之前設定 ''' #方法介紹 p.start()#開啟程序,使用run()方法 p.run()#程序啟動時執行的方法是它去呼叫target指定的目標 p.terminate()#強制終止程序,不會進行清理操作 p.is_alive()#判斷程序是否存活 p.join([timeout])#讓在join方法之後的程序等待,直到p程序執行結束 """ 強調:不同的作業系統建立程序的要求不一樣 在windows中建立程序是以匯入模組的方式進行 所以建立程序的程式碼必須寫在__main__子程式碼中 否則會直接報錯 因為在無限制建立程序 在linux和mac中建立程序是直接拷貝一份原始碼然後執行 不需要寫在__main__子程式碼中 """
第一種方法
from multiprocessing import Process import time def test(): print('=======>') time.sleep(3) print('=======>') def test1(name): print(f'{name}') time.sleep(3) print('=======>') if __name__ == '__main__': p = Process(target=test) # p = Process(target=test1, args=('春遊去動物園',)) p.start() print('主')
第二種方法
from multiprocessing import Process class Test(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print(self.name) if __name__ == '__main__': p = Test('test') p.start() print('主') #自定義類主要是改寫他的run()方法,run()方法在呼叫start()方法時一定會被呼叫
join
'''join讓主程序等待子程序結束之後,再執行主程序。'''
from multiprocessing import Process
import time
def task(name):
print(f"{name} is running")
time.sleep(2)
print(f"{name} is gone")
if __name__ == "__main__":
p = Process(target=task, args=("春遊去動物園",)) # 建立一個程序物件
p.start()
# p.join()
print(111)
print("主開始")
111
主開始
春遊去動物園 is running
春遊去動物園 is gone
from multiprocessing import Process
import time
def task(name):
print(f"{name} is running")
time.sleep(2)
print(f"{name} is gone")
if __name__ == "__main__":
p = Process(target=task, args=("春遊去動物園",)) # 建立一個程序物件
p.start()
p.join()
print(111)
print("主開始")
春遊去動物園 is running
春遊去動物園 is gone
111
主開始
'''一般而言,主程式中如果單為一句print,則優先執行print語句(如果執行語句夠多,則可見子程序執行)'''
from multiprocessing import Process
import time
def task(name):
print(f"{name} is running")
time.sleep(2)
print(f"{name} is gone")
if __name__ == "__main__":
p = Process(target=task,args=("春遊去動物園",)) # 建立一個程序物件
p.start()
# p.join()
print(111)
time.sleep(0.5)
print("主開始")
111
春遊去動物園 is running
主開始
春遊去動物園 is gone
'''如果程式中有連續多個join函式,則只有最先的join是起作用的'''
from multiprocessing import Process
import time
def task(name,sec):
print(f"{name} is running")
time.sleep(sec)
print(f"{name} is gone")
if __name__ == "__main__":
start_time = time.time()
p = Process(target=task,args=("常",3)) # 建立一個程序物件
p1 = Process(target=task,args=("辛",5)) # 建立一個程序物件
p2 = Process(target=task,args=("王",1)) # 建立一個程序物件
p.start()
p1.start()
p2.start()
# join只針對主程序,如果join下面多次join他是不阻塞的
p.join()
p1.join()
p2.join()
print(f"主{time.time()-start_time}")
辛 is running
常 is running
王 is running
王 is gone
常 is gone
辛 is gone
主5.103694438934326
# 注:如果不是連續多個,比如這樣:
from multiprocessing import Process
import time
def task(name,sec):
print(f"{name} is running")
time.sleep(sec)
print(f"{name} is gone")
if __name__ == "__main__":
start_time = time.time()
p = Process(target=task,args=("常",3)) # 建立一個程序物件
p1 = Process(target=task,args=("辛",5)) # 建立一個程序物件
p2 = Process(target=task,args=("王",1)) # 建立一個程序物件
p.start()
p.join()
p1.start()
p1.join()
p2.start()
p2.join()
print(f"主{time.time()-start_time}")
常 is running
常 is gone
辛 is running
辛 is gone
王 is running
王 is gone
主9.267089128494263
# 這樣就會先執行p程序,再執行p1程序,再執行p2程序
程序間資料預設隔離
from multiprocessing import Process
a=100
def test():
global a
a=50
print(a) # 50
if __name__ == '__main__':
p = Process(target=test)
p.start()
print(a) # 100
100
50
程序物件屬性和方法
檢視程序號
'''第一種current_process函式'''
from multiprocessing import Process,current_process
a=100
def test():
global a
a=50
print(a) # 50
if __name__ == '__main__':
p = Process(target=test)
p.start()
print(current_process()) # 獲取程序的名稱 <_MainProcess name='MainProcess' parent=None started>
print(current_process().pid) # 獲取程序的id 8560
print(a) # 100
'''第二種os模組'''
import os
from multiprocessing import Process,current_process
a=100
def test():
global a
a=50
print(a) # 50
if __name__ == '__main__':
p = Process(target=test)
p.start()
print(os.getpid()) # 9748 獲取當前程序的程序號
print(os.getppid()) # 5220 獲取當前程序的父程序號
print(a) # 100
os.getpid() # 獲取當前程序的程序號
os.getppid() # 獲取當前程序的父程序號
殺死子程序
terminate():不管任務是否完成,立即終止子程序
from multiprocessing import Process,current_process
import time
a=100
def test():
global a
time.sleep(3)
a=50
print(a)
if __name__ == '__main__':
p = Process(target=test)
p.start()
p.terminate()
print(a)
100
判斷子程序是否存活
is_alive():判斷程序子程序是否還在活著
from multiprocessing import Process,current_process
import time
a=100
def test():
global a
time.sleep(3)
a=50
print(a)
if __name__ == '__main__':
p = Process(target=test)
p.start()
print(p.is_alive()) # True
p.terminate() # 殺死程序
time.sleep(0.5)
print(p.is_alive()) # False
殭屍程序與孤兒程序
殭屍程序
殭屍程序:一個程序使用fork建立子程序,如果子程序退出,而父程序並沒有呼叫wait或waitpid獲取子
程序的狀態資訊,那麼子程序的程序描述符仍然儲存在系統中。這種程序稱之為僵死程序。
'''
僵死程序:子程序退出後,會將該程序的重型資源釋放掉(cpu,記憶體,開啟的檔案),子程序的進
程描述符仍然儲存在系統中,比如pid。
'''
所有的子程序在執行結束之後都會變成殭屍程序(死了沒死透)
程式正常結束才會產生殭屍程序,如果強制關閉父程序,作業系統會把父程序已經執行結束的子程序全部
刪除,也就不會產生殭屍程序了。
殭屍程序的危害:
系統的pid號是有限的,殭屍程序保留的資訊如果一直不被釋放,一直累計會導致沒有可用的pid號而
導致系統不能產生新的程序
孤兒程序
孤兒程序(無害):一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒
程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。
'''
子程序存活著 父程序意外死亡
子程序會被作業系統自動接管(兒童福利院)
'''
守護程序
正常情況下,主程序預設等待子程序呼叫結束之後結束
守護程序在主程序執行程式碼結束後,自動終止
"""
守護即死活全部參考守護的物件
物件死立刻死
"""
主程序程式碼執行完畢,守護程序就會結束
from multiprocessing import Process
import os, time
def task():
print("程序%s開啟" % os.getpid())
time.sleep(10)
print("程序%s結束" % os.getpid())
if __name__ == '__main__':
p = Process(target=task,daemon=True) # 在建立程序時也可以設定daemon,True為開啟守護程序,預設為False。
#p.daemon = True # 這一行程式碼會把子程序變成守護程式碼,主程序執行完,子程序也就執行完了,不會列印程序結束的那行程式碼
p.start()
print("主:%s" % os.getpid())
time.sleep(3)
主:6812
程序10096開啟
互斥鎖
互斥鎖的概念
互斥鎖: 對共享資料進行鎖定,保證同一時刻只能有一個執行緒去操作。
注意:
互斥鎖是多個執行緒一起去搶,搶到鎖的執行緒先執行,沒有搶到鎖的執行緒需要等待,等互斥鎖使用完釋放
後,其它等待的執行緒再去搶這個鎖。
互斥鎖的意思就是互相排斥,我們可以把多個程序比喻多個人,互斥鎖的工作原理就是多個人去爭搶共同
的一個資源:如多個人要上同一個衛生間,一個人搶到衛生間後上一把鎖,其他人都有外面等著,等到這個人
完成後解鎖後,其他人又可以去爭奪。所以互斥鎖的原題,就是把某一功能併發改序列,雖然降低了效率,但
保證了資料安全不錯亂。
"""
鎖相關知識
行鎖:針對行資料加鎖 同一時間只能一個人操作
表鎖:針對表資料加鎖 同一時間只能一個人操作
鎖的應用範圍很廣 但是核心都是為了保證資料的安全!!!
"""
互斥鎖的使用
from multiprocessing import Process,Lock
# 建立鎖
mutex = Lock()
# 上鎖
mutex.acquire()
...這裡編寫程式碼能保證同一時刻只能有一個執行緒去操作, 對共享資料進行鎖定...
# 釋放鎖
mutex.release()
注意點:
acquire和release方法之間的程式碼同一時刻只能有一個執行緒去操作
如果在呼叫acquire方法的時候 其他執行緒已經使用了這個互斥鎖,那麼此時acquire方法會堵塞,直到
這個互斥鎖釋放後才能再次上鎖。
互斥鎖與join()的區別
互斥鎖與join()的區別:
大前提:二者的原理都是一樣,都是將併發變成序列,從而保證有序
區別一:join是按照人為指定的順序執行,而互斥鎖是所有程序平等地競爭,誰先搶到誰先執行。
區別二:互斥鎖可以讓一部分程式碼(修改共享資料的程式碼)序列,而join只能將程式碼整體序列。
搶票小案例
# db.json檔案中的內容為{"count": 100}
from multiprocessing import Process, Lock
import json, time
import random
def search(name):
dic = json.load(open('db.json', 'r', encoding='utf-8'))
time.sleep(random.randint(0, 2)) # 模擬網路延遲
print('%s查到剩餘的票數為%s張' % (name, dic['count']))
def get(name):
dic = json.load(open('db.json', 'r', encoding='utf-8'))
time.sleep(0.3) # 模擬網路延遲
if dic['count'] > 0:
time.sleep(0.1) # 模擬網路延遲
print('%s成功買到了剩下的第%s票' % (name, dic['count']))
dic['count'] -= 1
json.dump(dic, open('db.json', 'w', encoding='utf-8'))
def rob_ticket(name, lock):
search(name)
with lock: # 相當於獲得了lock.acquire(),執行程式碼體完,自動執行lock.release()
get(name)
if __name__ == '__main__':
lock = Lock()
for i in range(10):
name = '路人%s' % i
p = Process(target=rob_ticket, args=(name, lock))
p.start()
執行結果:
路人0查到剩餘的票數為5張
路人1查到剩餘的票數為5張
路人8查到剩餘的票數為5張
路人7查到剩餘的票數為5張
路人9查到剩餘的票數為5張
路人0成功買到了剩下的第5票
路人1成功買到了剩下的第4票
路人2查到剩餘的票數為5張
路人4查到剩餘的票數為5張
路人3查到剩餘的票數為5張
路人6查到剩餘的票數為5張
路人8成功買到了剩下的第3票
路人7成功買到了剩下的第2票
路人5查到剩餘的票數為5張
路人9成功買到了剩下的第1票