Python並行系統工具_程式退出和程序間通訊
函式在呼叫時可以讓程式提前結束
-
-
其實只是丟擲了一個
SystemExit
異常-
Exit the interpreter by raising SystemExit(status)
-
-
可以捕獲異常,攔截程式的過早退出並執行清理活動
-
如果未被捕獲,則直譯器正常退出
-
raise
語句顯示丟擲SystemExit
異常和呼叫sys.exit
-
-
可以被捕獲
-
def later(): import sys print("Bye sys world") sys.exit(42) print("Never reached") if __name__ == '__main__': later() """ 程式執行結果 Bye sys world Process finished with exit code 42 """sys.exit
os.exit()
-
呼叫程序立即退出,而不是丟擲可以捕獲或忽略的異常
-
程序退出時不進行輸出流緩衝和執行清理處理器(由
atexit
模組定義) -
一般應當只在分支出的子程序上進行,最好不要在整個程式中進行
-
Exit to the system with specified status, without normal exit processing.
-
-
通過分支程序執行程式時,退出狀態可以在父程序中通過
os.wait
和os.watipid
os.system
和os.popen
執行shell名稱,在退出時獲取close()
,之後需要從位掩碼中提取(>>8
)
退出狀態實際上被包裝進返回值的特定位元位置,需要將結果右移8位元才能讀取
❌但是在windows中
,不需要進行右移,可以直接讀取
""" 分支子程序,用os.wait觀察其退出狀態 能在Unix上執行,但是在Windows上不能執行 派生執行緒共享全域性變數,但分支程序擁有自己的全域性變數副本 分支程序複製並因此共享檔案描述符 """ import os exit_stat = 0 def child(): global exit_stat exit_stat += 1 # 複製全域性變數,都是0,改變不影響其他分程序 print('Hello from Child', os.getpid(), exit_stat) os._exit(exit_stat) # 傳送到父程序的wait函式的退出狀態 print("Never reached") def parent(): while True: newpid = os.fork() if newpid == 0: child() else: pid, status = os.wait() print('Parent got', pid, status, (status >> 8)) if input() == 'q': break if __name__ == '__main__': parent() Hello from Child 10271 1 Parent got 10271 256 1 Hello from Child 10272 1 Parent got 10272 256 1 Hello from Child 10273 1 Parent got 10273 256 1 Hello from Child 10274 1 Parent got 10274 256 1 Hello from Child 10275 1 Parent got 10275 256 1退出狀態
-
-
但是可以呼叫
_thread.exit()
結束其呼叫執行緒 -
_thread.exit()
和sys.exit
相同,也會丟擲SystemExit
異常-
所以也可以線上程中使用
sys.exit
-
但是一定不要使用
os._exit()
,會產生奇怪的結果
-
-
執行緒一般不利用退出狀態,而是給模組水平的全域性變數複製或原位修改共享的可變物件以傳送訊號
-
-
簡單的檔案
-
命令列引數
-
程式退出狀態碼
-
shell環境變數
-
標準流重定向
-
os.popen
和subporcess
管理的管道流
-
-
Python庫提供的程序間通訊IPC工具
-
訊號允許程式向其他程式傳送簡單的通知時間
-
匿名管道允許共享檔案描述符的執行緒即相關程序傳遞資料,但是依賴於Unix下的程序分支模型
-
命名管道對映到系統的檔案系統,允許完全不相關的程式進行交流,但是並非所有的Python平臺都提供
-
套接字
雖然可以作為執行緒間通訊的手段,但在不共享記憶體空間的單獨程序的作用下,全部能力更能淋漓精緻的表現出來
管道更加偏向於在作業系統內部使用
-
讀取管道的呼叫一般會阻塞呼叫程式,直到資料變得可用,而非返回一個檔案尾指示器
-
管道的讀取呼叫總是返回最先寫入的資料(佇列,先進先出)
-
管道還可以用於同步化管獨立程式的執行
分類
-
具名管道
-
通常稱為FIFO,計算機上由一個檔案代表
-
具名管道是真正的外部檔案,進行通訊的程序無需相關,可以是獨立的啟動程式
-
-
匿名管道
-
僅在程序內部存在, 通常和程序分支合用,作為一種在應用程式內部連線父程序與子程序的手段
-
父程序與子程序通過共享的管道檔案描述符進行交流,後者為派生的程序所繼承
-
匿名管道對執行緒也適用
-
匿名管道
-
返回的是兩端的描述符,分別代表著輸入端和輸出端
-
但是沒有具體的分別
-
只是一端寫入,另一端就只能讀取
-
-
描述符的讀寫工具分別是
os.read
和os.write
,傳遞的是位元組字串 -
讀取資料時,不會考慮資料本身是什麼,只會按照指定的數量讀取資料
-
為了區分資訊,在管道中要求一個分割字元
-
可以使用
os.fdopen
將檔案描述符封裝進一個檔案物件 -
通過檔案物件的
readline
方法在管道中搜索寫一個/n
(read_fd, write_fd) = os.pipe()
## 匿名管道和程序分支 import os, time def child(pipeout): zzz = 0 while True: time.sleep(zzz) msg = ("Spam %03d" %zzz).encode() os.write(pipeout, msg) zzz = (zzz + 1) % 5 # 0,1,2,3,4,0 .。。。 迴圈 def parant(): pipein, pipeout = os.pipe() if os.fork() == 0 : child(pipeout) else: while True: # 資料傳送完之前保持阻塞 line = os.read(pipein, 32) print("Parent %d got [%s] at %s" % (os.getpid(), line, time.time())) parant()匿名管道和程序分支
import os import time start_time = time.time() def child(pipeout): zzz = 0 while True: time.sleep(zzz) msg = ("Spam %03d\n" % zzz).encode() os.write(pipeout, msg) zzz = (zzz + 1) % 5 # 0,1,2,3,4,0 .。。。 迴圈 def parant(): pipein, pipeout = os.pipe() if os.fork() == 0: # 關閉輸入端 os.close(pipein) child(pipeout) else: # 監聽管道, os.close(pipeout) # 關閉輸出端 # 建立文字模式的檔案物件 pipein = os.fdopen(pipein) while True: # 資料傳送完之前保持阻塞 # line = os.read(pipein, 32) line = pipein.readline()[:-1] print("Parent %d got [%s] at %s" % (os.getpid(), line, time.time())) parant()檔案文字模式
## 匿名管道和執行緒 import os import time import threading start_time = time.time() def child(pipeout): zzz = 0 while True: time.sleep(zzz) msg = ("Spam %03d" % zzz).encode() os.write(pipeout, msg) zzz = (zzz + 1) % 5 # 0,1,2,3,4,0 .。。。 迴圈 def parent(pipein): while True: # 資料傳送完成之前保持阻塞 line = os.read(pipein, 32) print("Parent %d got [%s] at %s" % (os.getpid(), line, time.time() - start_time)) pi· parent(pipein)匿名管道和執行緒
## 匿名管道和執行緒import osimport timeimport threadingstart_time = time.time()
def child(pipeout): zzz = 0 while True: time.sleep(zzz) msg = ("Spam %03d" % zzz).encode() os.write(pipeout, msg) zzz = (zzz + 1) % 5 # 0,1,2,3,4,0 .。。。 迴圈
def parent(pipein): while True: # 資料傳送完成之前保持阻塞 line = os.read(pipein, 32) print("Parent %d got [%s] at %s" % (os.getpid(), line, time.time() - start_time))
pi·parent(pipein)