2010-02-26 169 views
4

我認爲這將是那些簡單的問題,但它讓我感到莫名其妙。Python的time.sleep - 永不醒來

[STOP PRESS:我說得對。解決方案被發現。看到答案。]

我正在使用Python的unittest框架來測試多線程應用程序。好而直截了當 - 我有5個左右的工作線程監視一個公共隊列,還有一個生產線程爲他們製作工作項目。生產者線程正在被一個測試用例觸發。

在這個測試中,只有一個任務被放入隊列。它在測試中所做的處理僅僅是真實處理的一個存根,所以工作線程會在真正完成任務之前進行5秒鐘的休眠以模擬經過的時間,並且線程將準備好接受另一個任務。

到代碼的片段是:

logging.info("Sleep starting") 
time.sleep(5) 
logging.info("Waking up") 

現在怪異的一部分。我看到「睡眠開始」日誌消息,但沒有喚醒消息。程序鎖定並且不響應鍵盤中斷(CTRL + C)。 CPU負載非常低。

我在Windows和Ubuntu(Python 2.6.2)中看到了同樣的問題。

我曾經思考過,如果發生異常並且被隱藏,所以我在第一行和第二行之間添加了「print 1/0」 - 我看到引發了除零錯誤。我把它移到睡眠之後,我從來沒有看到這個消息。

我想:「好吧,也許另一個線程正在嘗試同時記錄非常大的內容,它仍在緩衝,它在做什麼?」

那麼,到了這個時候,測試已經返回到unittest,它正在測試系統的狀態之前暫停等待線程前進。

logging.info("Test sleep starting") 
time.sleep(0.25) 
logging.info("Test waking up") 

哇,看起來很熟悉。它以完全相同的方式凍結!第一條日誌消息出現,第二條不是。

我最近做了一個重大的單位重寫,所以我不能聲稱「我沒有碰任何東西」,但在變化中我看不到任何不適。

可疑的地方:

  • 我使用Threading.Lock(因爲我不知道如何來思考GIL的安全,所以我堅持我知道,我什麼也看不見「deadlocky」大約包括。我的代碼。

  • 我是新來的Python的單元測試框架。是不是有什麼它與重定向日誌或類似的可能模擬這些症狀?

  • 不,我還沒有取代非標準時間模塊!

什麼會阻止線程喚醒?我還有什麼錯過了?

+0

也許只是使用睡眠(秒)。 (從進口睡眠時間開始) Iam不是python用戶 - 只是使用谷歌,無法想象time.sleep(秒)命令可以凍結整個程序,而不是隻停留5秒.. 也許問題存在其他地方?在不同的線程或東西.. – TheChange 2010-02-26 14:58:12

+0

@TheChange。我*我*使用完全相同的功能;只是一個簡單的命名空間差異。我也很難相信time.sleep()可以凍結整個程序(或者至少是兩個有問題的線程),這就是爲什麼我要求提示我應該在哪裏尋找。 – Oddthinking 2010-02-26 15:27:36

+0

兩個建議:對一個虛擬函數使用monkey-patch'time.sleep',或者對一個簡單的打印文件使用monkey-patch'logging.info'。嘗試兩種方法,看看有什麼有用的行爲改變。也可以嘗試在監視CPU的同時按住Ctrl-C(自動重複),看看是否可以檢測到該進程中的任何活動......這意味着它確實存在,但有些東西在吞嚥信號。 – 2010-02-26 20:09:00

回答

5

感嘆。

工作線程#1正在睡覺,然後醒來。然後它會記錄喚醒消息,並被阻止。一次只能記錄一個線程。

UnitTest線程正在休眠,然後喚醒。然後它會記錄喚醒消息,並被阻止。一次只能記錄一個線程。

第一個工作線程正在休眠時,工作線程未曾提及的問題#2正在悄悄地完成隊列中前一項的處理。它得到一個日誌聲明。其中一個參數是一個對象,並且str()被隱式調用。該對象上的str()函數有一個bug;它訪問它的一些數據成員時就會陷入僵局。死鎖發生在日誌函數處理過程中,因此保持日誌線程鎖定狀態,並使其看起來像其他線程永遠不會醒來。

由零測試劃分沒有什麼區別,因爲它的結果是一個嘗試登錄。

+1

很明顯,我不知道確實發生了這種情況,但日誌包中的變幻莫測(如鎖定的東西)經常讓我感到不適,這就是爲什麼我用'print'提出測試的原因。很高興聽到你找到它! :) – 2010-02-27 15:32:05

+0

https://twitter.com/#!/nedbat/status/194452404794691584 – 2012-05-01 10:23:29

+0

@Tim :-)哈!我在Ned Batchelder的twitter feed上看到了這一點,並且認爲「他一直在閱讀*我的* feed!」 https://twitter.com/#!/talios/statuses/157760121160744960 – Oddthinking 2012-05-01 12:12:17

-3

在Linux上,嘗試將I/O調度程序更改爲完全公平隊列(CFQ)。

echo cfq > /sys/block/sda/queue/scheduler 
+1

歡迎來到StackOverflow。如果你看看這個問題,你可以看到(a)這也發生在Windows上,所以這個僅限於Unix的解決方案將不起作用。 (b)問題出在Python線程上; Python有自己的線程調度,這些線程調度不會隨着操作系統級調度器的變化而修復。 (c)我已經發現了這個問題,而且這是一個死鎖,它不會在調度程序發生變化的情況下得到解決。 – Oddthinking 2012-05-01 12:07:32