歡迎光臨
我們一直在努力

深入 Python 多程序程式設計基礎

文章摘要: signal.SIGTERM) # 發一個SIGTERM訊號 time.sleep(5) # 父程序繼續休眠5s觀察子程序是否還存在 os.kill(pidprint ‘in father process’ time.sleep(5) # 父程序休眠5s再殺死子程序

多程序程式設計知識是Python程式設計師進階高階的必備知識點,我們平時習慣了使用multiprocessing庫來操縱多程序,但是並不知道它的具體實現原理。下面我對多程序的常用知識點都簡單列了一遍,使用原生的多程序方法呼叫,幫助讀者理解多程序的實現機制。程式碼跑在linux環境下。沒有linux條件的,可以使用docker或者虛擬機器執行進行體驗。

docker pull python:2.7

生成子程序

Python生成子程序使用 os.fork() ,它將產生一個子程序。fork呼叫同時在父程序和主程序同時返回,在父程序中返回子程序的pid,在子程序中返回0,如果返回值小於零,說明子程序產生失敗,一般是因為作業系統資源不足。

import os
def create_child():
pid = os.fork()
if pid > 0:
print 'in father process'
return True
elif pid == 0:
print 'in child process'
return False
else:
raise

生成多個子程序

我們呼叫 create_child 方法多次就可以生成多個子程序,前提是必須保證 create_child 是在父程序裡執行,如果是子程序,就不要在呼叫了。

# coding: utf-8
# child.py
import os

def create_child(i):
pid = os.fork()
if pid > 0:
print 'in father process'
return pid
elif pid == 0:
print 'in child process', i
return 0
else:
raise

for i in range(10): # 迴圈10次,建立10個子程序
pid = create_child(i)
# pid==0是子程序,應該立即退出迴圈,否則子程序也會繼續生成子程序
# 子子孫孫,那就生成太多程序了
if pid == 0:
break

執行 python child.py ,輸出

in father process
in father process
in child process 0
in child process 1
in father process
in child process 2
in father process
in father process
in child process 3
in father process
in child process 4
in child process 5
in father process
in father process
in child process 6
in child process 7
in father process
in child process 8
in father process
in child process 9

程序休眠

使用time.sleep可以使程序休眠任意時間,單位為秒,可以是小數

import time
for i in range(5):
print 'hello'
time.sleep(1) # 睡1s

殺死子程序

使用os.kill(pid, sig_num)可以向程序號為pid的子程序傳送訊號,sig_num常用的有SIGKILL(暴力殺死,相當於kill -9),SIGTERM(通知對方退出,相當於kill不帶引數),SIGINT(相當於鍵盤的ctrl+c)。

# coding: utf-8
# kill.py

import os
import time
import signal

def create_child():
pid = os.fork()
if pid > 0:
return pid
elif pid == 0:
return 0
else:
raise

pid = create_child()
if pid == 0:
while True: # 子程序死迴圈列印字串
print 'in child process'
time.sleep(1)
else:
print 'in father process'
time.sleep(5) # 父程序休眠5s再殺死子程序
os.kill(pid, signal.SIGKILL)
time.sleep(5) # 父程序繼續休眠5s觀察子程序是否還有輸出

執行 python kill.py ,我們看到控制檯輸出如下

in father process
in child process
# 等1s
in child process
# 等1s
in child process
# 等1s
in child process
# 等1s
in child process
# 等了5s

說明os.kill執行之後,子程序已經停止輸出了

殭屍子程序

在上面的例子中,os.kill執行完之後,我們通過ps -ef|grep python快速觀察程序的狀態,可以發現子程序有一個奇怪的顯示

root        12     1  0 11:22 pts/0    00:00:00 python kill.py
root 13 12 0 11:22 pts/0 00:00:00 [python] <defunct>

待父程序終止後,子程序也一塊消失了。那 是什麼含義呢?

它的含義是「殭屍程序」。子程序結束後,會立即成為殭屍程序,殭屍程序佔用的作業系統資源並不會立即釋放,它就像一具屍體啥事也不幹,但是還是持續佔據著作業系統的資源(記憶體等)。

收割子程序

如果徹底幹掉殭屍程序?父程序需要呼叫waitpid(pid, options)函式,「收割」子程序,這樣子程序纔可以灰飛煙滅。waitpid函式會返回子程序的退出狀態,它就像子程序留下的臨終遺言,必須等父程序聽到後才能徹底瞑目。

# coding: utf-8
import os
import time
import signal
def create_child():
pid = os.fork()
if pid > 0:
return pid
elif pid == 0:
return 0
else:
raise
pid = create_child()
if pid == 0:
while True: # 子程序死迴圈列印字串
print 'in child process'
time.sleep(1)
else:
print 'in father process'
time.sleep(5) # 父程序休眠5s再殺死子程序
os.kill(pid, signal.SIGTERM)
ret = os.waitpid(pid, 0) # 收割子程序
print ret # 看看到底返回了什麼
time.sleep(5) # 父程序繼續休眠5s觀察子程序是否還存在

執行python kill.py輸出如下

in father process
in child process
in child process
in child process
in child process
in child process
in child process
(125, 9)

我們看到waitpid返回了一個tuple,第一個是子程序的pid,第二個9是什麼含義呢,它在不同的作業系統上含義不盡相同,不過在Unix上,它通常的value是一個16位的整數值,前8位表示程序的退出狀態,後8位表示導致程序退出的訊號的整數值。所以本例中退出狀態位0,訊號編號位9,還記得 kill -9 這個命令麼,就是這個9表示暴力殺死程序。

如果我們將os.kill換一個訊號纔看結果,比如換成os.kill(pid, signal.SIGTERM),可以看到返回結果變成了 (138, 15) ,15就是SIGTERM訊號的整數值。

waitpid(pid, 0) 還可以起到等待子程序結束的功能,如果子程序不結束,那麼該呼叫會一直卡住。

捕獲訊號

SIGTERM訊號預設處理動作就是退出程序,其實我們還可以設定SIGTERM訊號的處理函式,使得它不退出。

# coding: utf-8
import os
import time
import signal
def create_child():
pid = os.fork()
if pid > 0:
return pid
elif pid == 0:
return 0
else:
raise
pid = create_child()
if pid == 0:
signal.signal(signal.SIGTERM, signal.SIG_IGN)
while True: # 子程序死迴圈列印字串
print 'in child process'
time.sleep(1)
else:
print 'in father process'
time.sleep(5) # 父程序休眠5s再殺死子程序
os.kill(pid, signal.SIGTERM) # 發一個SIGTERM訊號
time.sleep(5) # 父程序繼續休眠5s觀察子程序是否還存在
os.kill(pid, signal.SIGKILL) # 發一個SIGKILL訊號
time.sleep(5) # 父程序繼續休眠5s觀察子程序是否還存在

我們在子程序裡設定了訊號處理函式,SIG_IGN表示忽略訊號。我們發現第一次呼叫os.kill之後,子程序會繼續輸出。說明子程序沒有被殺死。第二次os.kill之後,子程序終於停止了輸出。

接下來我們換一個自定義訊號處理函式,子程序收到SIGTERM之後,列印一句話再退出。

# coding: utf-8
import os
import sys
import time
import signal
def create_child():
pid = os.fork()
if pid > 0:
return pid
elif pid == 0:
return 0
else:
raise
def i_will_die(sig_num, frame): # 自定義訊號處理函式
print "child will die"
sys.exit(0)
pid = create_child()
if pid == 0:
signal.signal(signal.SIGTERM, i_will_die)
while True: # 子程序死迴圈列印字串
print

未經允許不得轉載:頭條楓林網 » 深入 Python 多程序程式設計基礎