我注意到我的一些机器上的GHC.Conc中的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毫秒只有一次活动:
另一方面,这是来自我的一台机器的ThreadScope的截图,程序总共需要1秒钟:
类似的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;
}
做正确的事情,即运行'time ./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很好。
答案 0 :(得分:3)
threadDelay
不是一个准确的计时器。它保证你的线程至少会在 中休眠,只要它的参数说它应该,但它不会承诺更多。如果您想要定期发生某些事情,您将不得不使用其他东西。 (我不确定是什么,但Unix' realtime alarm signal可能适合你。)
答案 1 :(得分:1)
我怀疑你忘了使用'-threaded'选项进行编译。 (我为6.12.3做了一次,并且一直有30毫秒的线程延迟。)
答案 2 :(得分:1)
如上所述,threadDelay只提供一个保证,只要你请求,你就会等待至少。 Haskell的运行时没有从OS获得特殊的合作
除此之外,它是操作系统的最佳努力。
可能值得对threadDelays的结果进行基准测试。例如:
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 ()
在我的窗户框上,这给了我:
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进行计时,或至少检查时间并执行任何操作直到给定的时刻。
如果您愿意使用FFI,那么通过C进行的高精度睡眠可能对您有用,但成本是您需要使用绑定线程(至少为您的计时器)。