2022.4.19程序相關操作、方法、概念
阿新 • • 發佈:2022-04-19
2022.4.19程序相關操作、方法、概念
- 程式碼建立程序
- join方法
- 程序間資料預設隔離
- 程序物件相關屬性和方法
- 殭屍程序和孤兒程序
- 守護程序
- 互斥鎖(重要)
一、程式碼建立程序
問題:建立程序的方式有哪些?
(1)滑鼠雙擊桌面程式圖示
(2)程式碼建立
建立程序的本質:
在記憶體中申請一塊記憶體空間用於執行相應的程式程式碼
1、第一種(函式物件作為程序):
from multiprocessing import Process import time def task(name): print('%s is runing' % name) time.sleep(3) print('%s is over' % name) if __name__ == '__main__': p = Process(target=task, args=('json',)) # 建立程序物件,指定為task,args指定資料,必須加逗號以元組形式傳入 p.start() # 告訴系統建立一個新的程序 print('主程序') 注意: """ 強調:不同的作業系統建立程序的要求不一樣 在windows中建立程序是以匯入模組的方式進行 所以建立程序的程式碼必須寫在__main__子程式碼中 否則會直接報錯 因為在無限制建立程序 在linux和mac中建立程序是直接拷貝一份原始碼然後執行 不需要寫在__main__子程式碼中 """
2、第二種(類物件作為程序):
from multiprocessing import Process import time class MyProcess(Process): def __init__(self, username): self.username = username super().__init__() def run(self): print('你好啊 小姐姐',self.username) time.sleep(3) print('get out!!!',self.username) if __name__ == '__main__': p = MyProcess('tony') # 建立程序物件 p.start() # 建立程序 print('主程序') # 主程序 # 你好啊 小姐姐 tony # get out!!! tony 由此可見程序相當於進行非同步操作了
3、程序實現併發
將與客戶端通訊的程式碼封裝成一個函式
之後每來一個客戶端就建立一個程序專門做互動
服務端:
import socket from multiprocessing import Process # 封裝一個get_server,這個建立程序就不會反覆執行裡面的程式碼 # 從而避免報地址只允許使用一次的錯誤 def get_server(): server = socket.socket() server.bind(('127.0.0.1', 8080)) server.listen(5) return server # 將服務客戶端的程式碼封裝成函式(通訊程式碼) def talk(sock): while True: data = sock.recv(1024) print(data.decode('utf8')) sock.send(data.upper()) if __name__ == '__main__': server = get_server() while True: sock, addr = server.accept() p = Process(target=talk, args=(sock, )) p.start()
客戶端:
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))
while True:
client.send(b'hello big baby~')
data = client.recv(1024)
print(data.decode('utf8'))
二、join方法
作用:讓主程序程式碼等待子程序程式碼執行完畢再執行
from multiprocessing import Process
import time
def task(name, n):
print(f'{name} is runing')
time.sleep(n)
print(f'{name} is over')
if __name__ == '__main__':
p1 = Process(target=task, args=('jason', 1))
p2 = Process(target=task, args=('tony', 2))
p3 = Process(target=task, args=('kevin', 3))
start_time = time.time()
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
end_time = time.time() - start_time
print('主程序', f'總耗時:{end_time}') # 主程序 總耗時:3.015652894973755,因為上面程序已經陸續開始,因此各程序是相互重疊的
# 如果是一個start一個join交替執行 那麼總耗時就是各個任務耗時總和,因為join方法會讓子程序執行完再執行主程序
三、程序間資料預設隔離
引入:記憶體可以看成很多個小隔間,彼此互不干擾
from multiprocessing import Process
money = 999
def task():
global money # 區域性修改全域性不可變型別
money = 666
print(money)
if __name__ == '__main__':
p = Process(target=task)
p.start()
p.join() # 確保子程序程式碼執行結束再列印money
print(money)
結果:
666
999
"""預設隔離 但是可以通過一些技術打破,後面會了解"""
四、程序物件屬性和方法
如何檢視程序號?
# windows系統:
tasklist結果集中PID
# mac系統
ps -ef
# python程式碼檢視
1.current_process函式
from multiprocessing import Process, current_process
current_process().pid
# 獲取程序號的用處之一就是可以通過程式碼的方式管理程序
windows taskkill關鍵字
mac/linux kill關鍵字
2.os模組
os.getpid() # 獲取當前程序的程序號
os.getppid() # 獲取當前程序的父程序號
# 殺死子程序
terminate()
# 判斷子程序是否存活
is_alive()
五、殭屍程序和孤兒程序
1、殭屍程序
問題:為什麼主程序預設要等待子程序執行結束返回結果才會結束?
答:所有的子程序在執行結束之後都會變成殭屍程序(死了沒死透),
還保留著pid和一些執行過程中的記錄便於主程序檢視(短時間儲存),
這些資訊會被主程序回收時殭屍程序徹底死亡,以下情況將殺死執行結束後的殭屍程序
1.主程序正常結束
2.呼叫join方法
2、孤兒程序
狀態:子程序存活著,父程序意外死亡
子程序會被作業系統自動接管(eg:兒童福利院)
六、守護程序
守護程序:
即設定為守護程序的程序物件,會隨著守護的程序物件結束而結束
from multiprocessing import Process
import time
def task(name):
print(f'大內總管:{name}正常活著')
time.sleep(3)
print(f'大內總管:{name}正常死了')
if __name__ == '__main__':
p = Process(target=task, args=('趙公公',))
p.daemon = True # 將p設為守護程序
p.start()
print('皇帝壽終正寢了啊')
# 結果:
皇帝壽終正寢了啊
大內總管:趙公公正常活著 # 只打印一個出來,因為主程序死亡,守護程序還沒執行完,直接結束
七、互斥鎖(重要)
引入(搶票問題):
為什麼手機上明明顯示還有餘票,但是點選購買的時候卻提示已經沒有票了,之後回到查詢頁面再查詢發現確實沒票了。
為什麼:
因為你某一時間開啟買票軟體檢視票數,看的是這一時間的資料,只要不重新整理不點選下一步,展示的永遠是這個時間的資料
那麼這個是如何實現的呢,這個就需要使用互斥鎖了。
程式碼模擬:
import json
from multiprocessing import Process
import time
import random
# ticket_data.json檔案內容:{"ticket_num": 0}
# 查票
def search(name):
with open(r'ticket_data.json', 'r', encoding='utf8') as f:
data = json.load(f)
print(f'{name}查詢當前餘票:%s' % data.get('ticket_num'))
# 買票
def buy(name):
# 1.點選買票是需要再次查票的 因為期間其他人可能已經把票買走
with open(r'ticket_data.json', 'r', encoding='utf8') as f:
data = json.load(f)
time.sleep(random.randint(1, 3))
# 2.判斷是否還有餘票
if data.get('ticket_num') > 0:
data['ticket_num'] -= 1
with open(r'ticket_data.json', 'w', encoding='utf8') as f:
json.dump(data, f)
print(f'{name}搶票成功')
else:
print(f'{name}搶票失敗 沒有餘票了')
def run(name):
search(name)
buy(name)
# 模擬多人同時搶票
if __name__ == '__main__':
for i in range(1, 10):
p = Process(target=run, args=('使用者:%s' % i,))
p.start()
# 問題:
上面模擬了買票的基本邏輯,但是有一個問題,如果同時搶票的話,多個子程序獲得的資料都一樣,都會顯示搶票成功,造成資料的錯亂然,怎麼辦?
答:併發--->序列(犧牲效率保證資料安全)--->互斥鎖
# 注意:
1.互斥鎖並不能輕易使用 容易造成死鎖現象
2.互斥鎖只在處理資料的部分加鎖,不能什麼地方都加,嚴重影響程式的效率
互斥鎖:
from multiprocessing import Process, lock
mutex = lock() # 定義互斥鎖
mutex.acquire() # 搶鎖
mutex.release() # 放鎖
結合搶票軟體,我們應該把鎖加在購買票的時候
def run(name, mutex):
search(name)
# 只需要把買票環節變成序列即可
mutex.acquire() # 搶鎖
buy(name)
mutex.release() # 放鎖
注意:加鎖購買之後一定要放鎖,不然會一直卡在這個使用者手裡
鎖相關知識:
鎖相關知識
行鎖:針對行資料加鎖 同一時間只能一個人操作
表鎖:針對表資料加鎖 同一時間只能一個人操作
鎖的應用範圍很廣 但是核心都是為了保證資料的安全!!!