【问题标题】:Why doesn't sleep work?为什么睡眠不起作用?
【发布时间】:2015-02-03 08:19:14
【问题描述】:

为什么c_sleep在下面的代码中立即返回?

{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Types
import Data.Time.Clock
import Control.Concurrent

foreign import ccall unsafe "unistd.h sleep" 
    c_sleep :: CUInt -> IO CUInt

main :: IO ()
main = do
    getCurrentTime >>= print . utctDayTime
    c_sleep 10     >>= print                -- this doesn't sleep
    getCurrentTime >>= print . utctDayTime
    threadDelay $ 10 * 1000 * 1000          -- this does sleep
    getCurrentTime >>= print . utctDayTime
$ ghc --make Sleep.hs && ./Sleep
[1 of 1] 编译 Main ( Sleep.hs, Sleep.o )
连接睡眠...
29448.191603s
10
29448.20158s
29458.211402s

$ ghc --版本
Glorious Glasgow Haskell 编译系统,版本 7.8.3

$ cabal --版本
cabal 安装版本 1.20.0.3
使用 Cabal 库的 1.20.0.0 版本

注意:实际上,我想在 C 代码中使用 sleep 来模拟函数 func 中的一些繁重计算,并在 Haskell 中调用 that 函数,但这也不起作用,可能出于相同的原因。

【问题讨论】:

  • GHC 运行时使用的信号可能会破坏sleep。你检查过错误代码吗?也许你应该将它包装在一个循环中以在它被中断时重新启动它(在这种情况下,最好使用nanosleep 以获得更高的精度)。
  • @Rufflewind:sleep 不返回错误代码,而是以秒为单位的未睡眠量:/。还没试过nanosleep,但是usleep也不管用。
  • 错误代码通过errno变量“返回”。将(c_sleep 10) 包裹在throwErrnoIf (/= 0) "sleep" 中,您会看到它被中断了。
  • 另见this bug。与这个问题不完全相关,但它确实说 RTS,即使是单线程的,也会定期发送信号,这将中断任何 sleep 调用。
  • @Rufflewind:谢谢。天哪,为什么man 3 sleep 没有提到errno 更新?我想我也会写一个void nanosleep_loop(uint32_t),因为nanosleep 也会受到影响。这张票可能回答了这个问题,所以请随意添加。

标签: haskell ghc ffi


【解决方案1】:

GHC 的 RTS appears to use signalsown purposes,这意味着睡眠很快就会被这些信号之一打断。我也不认为这是一个错误,运行时是come with its own territory,可以这么说。 Haskellian 的方法是使用threadDelay,但C 程序在没有一些技巧的情况下访问它并不容易。

proper way 将在其他信号中断的情况下反复恢复睡眠。我建议使用nanosleep,因为sleep 的精度只有几秒,而且信号出现的频率似乎比这要高得多。

#include <errno.h>
#include <time.h>

/* same as 'sleep' except it doesn't get interrupted by signals */
int keep_sleeping(unsigned long sec) {
    struct timespec rem, req = { (time_t) sec, 0 }; /* warning: may overflow */
    while ((rem.tv_sec || rem.tv_nsec) && nanosleep(&req, &rem)) {
        if (errno != EINTR) /* this check is probably unnecessary */
            return -1;
        req = rem;
    }
    return 0;
}

【讨论】:

    【解决方案2】:

    所有并发原语总是有一个 clawback 声明,它们可能阻塞的时间少于指定的时间 - 它们可能会虚假返回。这与语言无关,这是并发的本质,所以如果你想精确地等待指定的时间,在任何语言中你都需要在睡眠后构造一个循环检查时钟。

    【讨论】:

    • 虽然这是真的,但人们不会期望通常在其单线程 C 域中运行良好的有效 (u)sleep() 调用会在调用后立即中断。如果一个人不使用-threaded 和/或并发,那就更是如此。
    • @Zeta 那么期望是错误的。没有人指定虚假唤醒的原因 - 它是spurious;它没有说"may wake up *only* because the application interrupted itself"。我认为针对未指定的行为直观地编码是没有意义的——就像我们不假设未初始化的 int 在 C 中为零。
    猜你喜欢
    • 2012-08-27
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 2019-01-21
    • 1970-01-01
    相关资源
    最近更新 更多