2012-03-14 59 views
7

我用非常標準的Threading.Event: 主線程獲取到一個點,它在運行一個循環:Python的更好的解決方案Threading.Event半忙等待

event.wait(60) 

其他塊上直到一個答覆的要求可再啓動:

event.set() 

我希望主線程,選擇40秒,但這種情況並非如此。 從Python 2.7源庫/ threading.py:

# Balancing act: We can't afford a pure busy loop, so we 
# have to sleep; but if we sleep the whole timeout time, 
# we'll be unresponsive. The scheme here sleeps very 
# little at first, longer as time goes on, but never longer 
# than 20 times per second (or the timeout time remaining). 
endtime = _time() + timeout 
delay = 0.0005 # 500 us -> initial delay of 1 ms 
while True: 
    gotit = waiter.acquire(0) 
    if gotit: 
     break 
    remaining = endtime - _time() 
    if remaining <= 0: 
     break 
    delay = min(delay * 2, remaining, .05) 
    _sleep(delay) 

我們得到的是一個系統調用選擇運行每500US。 這會導致機器上的顯着負載,並且選擇循環相當緊密。

有人可以請解釋爲什麼有一個平衡的行爲,爲什麼它不同於等待文件描述符的線程。

第二,有沒有更好的方法來實現一個主要睡眠主線程沒有這樣一個緊密的循環?

回答

3

我最近遇到了同樣的問題,並且我還將其跟蹤到threading模塊中的確切代碼塊。

它很爛。

解決方案是重載線程模塊,或者遷移到python3,這部分實現已經修復。

就我而言,遷移到python3會是一個巨大的努力,所以我選擇了前者。我所做的是:

  1. 我創建了一個快速.so文件(使用cython)與接口pthread。它包括調用相應的pthread_mutex_*函數的python函數,以及與libpthread的鏈接。具體而言,與我們感興趣的任務最相關的功能是pthread_mutex_timedlock
  2. 我創建了一個新的threading2模塊,(並用我的代碼庫中的所有import threading行替換爲import threading2)。在threading2,我從threadingLockConditionEvent)從Queue我用了很多(QueuePriorityQueue)重新定義的所有相關的類,並且還的。 Lock類使用pthread_mutex_*函數完全重新實現,但其餘的更容易 - 我只是將原始類(例如threading.Event)和覆蓋__init__的子類別創建爲我的新Lock類型。剩下的只是工作。

Lock類型的實現是非常相似threading原始的實現,但我根據的acquire對我python3threading模塊(發現代碼中的新實行這,自然是要簡單得多比上述「平衡行爲」塊)。這部分相當簡單。

(順便說一下,在我的情況下,結果是我的大規模多線程進程的速度提高了30%,甚至超過我的預期。)

2

我完全同意你的說法,這是跛腳。

目前,我堅持一個簡單的選擇調用,沒有超時,並監聽之前創建的管道。 喚醒是通過在管道中寫入一個字符完成的。

請參閱這個sleepwakeup功能來自gunicorn。

相關問題