[Python] Thread.start() 後沒有執行到 thread function

[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 一定會被執行到喔~

 

(本頁面已被瀏覽過 978 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料