2011-09-15 29 views
5

我注意到我的一些機器上的GHC.Conc中的threadDelay函數有奇怪的行爲。以下程序:Ubuntu中的Haskell(GHC)中的ThreadDelay問題

main = do print "start" 
      threadDelay (1000 * 1000) 
      print "done" 

需要1秒鐘才能按預期運行。在另一方面,這個程序:

{-# LANGUAGE BangPatterns #-} 
import Control.Concurrent 

main = do print "start" 
      loop 1000 
      print "done" 
    where loop :: Int -> IO() 
     loop !n = 
      if n == 0 
      then return() 
      else do threadDelay 1000 
        loop (n-1) 

大約需要10秒,在兩個我的機器上運行,但在其他計算機上需要1秒左右,符合市場預期。 (我使用'-threaded'標記編譯了上述兩個程序。)以下是ThreadScope的屏幕截圖,顯示每10毫秒只有一次活動:Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for 10 milliseconds.

另一方面,從我的一臺機器ThreadScope上的程序需要1秒的總:Screenshot of ThreadScope showing that threadDelay of 1 millisecond sleeps for about 1 milliseconds.

類似的C程序:

#include <unistd.h> 
#include <stdio.h> 

int main() { 
    int i; 
    for (i=1; i < 1000; i++) { 
    printf("%i\n",i); 
    usleep(1000); 
    } 
    return 0; 
} 

做正確的事,即運行的時間./a.out「給像輸出:

1 
2 
... 
999 

real 0m1.080s 
user 0m0.000s 
sys 0m0.020s 

有沒有人遇到過這個問題,如果是的話,這個問題怎麼解決?我在我所有的機器上運行ghc 7.2.1 for Linux(x86_64),並運行着各種版本的Ubuntu。它在Ubuntu 10.04.2上效果不錯,但在11.04上很好。

回答

3

threadDelay不是一個準確的計時器。它承諾,只要它的論據說明它應該,你的線程就會睡,至少,但它不能承諾更多。如果你想定期發生某些事情,你將不得不使用別的東西。 (我不知道是什麼,但可能Unix' realtime alarm signal會爲你工作。)

1

我懷疑你忘了用'-threaded'選項編譯。 (我曾經爲6.12.3做過一次,並且一直有30毫秒的線程延遲。)

+0

我確實編譯了'-threaded'標誌。 – Andreas

1

如上所述,threadDelay只做出一個保證,這就是說,只要您請求,您將等待至少。 Haskell的運行時不能從操作系統獲得特別的合作。除此之外,這是操作系統的最大努力。

這可能是有價值的基準線程延遲的結果。例如:

module Main where 
import Control.Concurrent 
import Data.Time 

time op = 
    getCurrentTime >>= \ t0 -> 
    op >> 
    getCurrentTime >>= \ tf -> 
    return $! (diffUTCTime tf t0) 

main :: IO() 
main = 
    let action tm = time (threadDelay tm) >>= putStrLn . show in 
    mapM action [2000,5000,10000,20000,30000,40000,50000] >> 
    return() 

在我的Windows中,這給了我:

0.0156098s 
0.0156098s 
0.0156098s 
0.0312196s 
0.0312196s 
0.0468294s 
0.0624392s

這表明延遲的組合和getCurrentTime有15.6毫秒的分辨率。當我循環1000次延遲1000時,我最終等待15.6秒,所以這只是線程的最小等待時間。

在我的Ubuntu盒子(11.04,內核2.6.38-11)上,我獲得了更高的精度(〜100us)。

這可能是因爲您可以通過保持程序繁忙來避免計時問題,所以我們不需要上下文切換。無論如何,我建議你不要使用threadDelay進行計時,或者至少檢查時間並在給定的時刻執行任何操作。

通過C的高精度睡眠可能對您有用,如果您願意使用FFI,但費用是您需要使用綁定線程(至少對於您的計時器)。