[Python] Thread.start() 後沒有執行到 thread function
今天在 debug 一個 python 程式的問題,
原本預期 thread 的函式要被執行到,但卻似乎沒有被跑到…
下面是一個很簡單的例子,首先建一個 threading.Thread 物件後,就 start() 它,
預期 _execmd() 會被執行到,將 A 的 instance 設定一個叫 process 的屬性:
import subprocess import threading import time class A(object): def __init__(self): pass @staticmethod def _execmd(obj): subprocess.Popen() print "In execmd" obj.process = "p" time.sleep(10) def run_exe_cmd(self): thread = threading.Thread(target=self._execmd, args=(self,)) thread.daemon = True thread.start() thread.join(5) if thread.is_alive(): print "thread is still alive", self.process A().run_exe_cmd()
不過這個程式平常執行沒什麼問題,昨天卻突然出錯了一次,
self.process 說沒有 process 這個屬性… 是怎麼回事呢?
看了一下程式,唯一有可能的就是 target 裡指定的 _execmd 還沒執行到,
或是執行到了但還沒跑到 obj.process = “p” 那一行,5 秒鐘的 timeout 就到了…
現在看起來有兩個可能的原因:
1. CPU 卡住
這個是後來查出來真正的原因,因為 CPU 卡住了快一分鐘,因此所有的 CPU 指令都不會動,
因此等 CPU 回復正常時,很快就 timeout 了,所以還沒跑到設定 process 屬性那一行。
2. 系統時間改變
這個並不是這次 case 的起因,不過也可以做出相同的效果。
如果去查 is_alive() 這個函式的話,可以看到它是去看 __started 這個 Event 是不是已經被設定:
def isAlive(self): """Return whether the thread is alive. This method returns True just before the run() method starts until just after the run() method terminates. The module function enumerate() returns a list of all alive threads. """ assert self.__initialized, "Thread.__init__() not called" return self.__started.is_set() and not self.__stopped is_alive = isAlive
而 __started 被設定的時間,是在 start -> __bootstrap() -> __bootstrap_inner() 之後,
先設定好 __started,再呼叫 run(),因此是有機會 is_alive() == True 但 run() 還沒執行到:
def start(self): try: _start_new_thread(self.__bootstrap, ()) except Exception: ...... def __bootstrap(self): try: self.__bootstrap_inner() except: ...... def __bootstrap_inner(self): try: self.__started.set() try: self.run() except SystemExit: ......
測試這個猜想也很容易,在 _execmd() 前面加個 sleep(),
接著執行程式之後,立刻修改系統時間到未來的 5 分鐘,就會發現 join() 函式立刻返回了,
此時 is_alive() == True,但 process 屬性還沒有被設定,因此就出現問題囉~
結論是:如果有用 join() 的話,不能假設 thread function 一定會被執行到喔~