阻止直到按键或给定时间

时间:2012-02-04 17:58:08

标签: haskell

如何阻止(1)按键之前或(2)达到hh:mm格式的先前输入时间。我正在使用Windows以防万一。这个DOS assembler program(也可以在Windows上运行)通过Windows控制台中的batchman waittil 16:30之类的东西做我想要的但是我想在Haskell中完全做到这一点,(没有利用该计划。)

1 个答案:

答案 0 :(得分:4)

你可以开始两个线程:一个读取一个字符,另一个等待直到达到指定的时间;他们都写一个MVar来表示完成。

这有点棘手,但主要是由于细节:我们希望stdin处于无缓冲和非回显模式,以便单个按键停止等待而不打印任何内容,然后恢复原始状态然后;并且我们还需要在完成后杀死两个线程,以便我们例如在超时到期后停止从stdin读取。此外,如果发生异常,我们需要确保正确清理事物。 bracket在这里简化了清理逻辑,但它仍然非常难看:

import Prelude hiding (catch)
import Control.Exception
import Control.Concurrent
import System.IO

withRawStdin :: IO a -> IO a
withRawStdin = bracket uncook restore . const
  where
    uncook = do
        oldBuffering <- hGetBuffering stdin
        oldEcho <- hGetEcho stdin
        hSetBuffering stdin NoBuffering
        hSetEcho stdin False
        return (oldBuffering, oldEcho)
    restore (oldBuffering, oldEcho) = do
        hSetBuffering stdin oldBuffering
        hSetEcho stdin oldEcho

waitFor :: Int -> IO ()
waitFor delay = do
    done <- newEmptyMVar
    withRawStdin . bracket (start done) cleanUp $ \_ -> takeMVar done
  where
    start done = do
        t1 <- forkIO $ getChar >> putMVar done ()
        t2 <- forkIO $ threadDelay delay >> putMVar done ()
        return (t1, t2)
    cleanUp (t1, t2) = do
        killThread t1
        killThread t2

即便如此,此解决方案仍然无法处理等待特定时间 - 只需等待一定数量的微秒。为了将一天中的时间转换为睡眠的数微秒,this previous SO question可能有所帮助。如果睡眠足够长,那么它们可能不适合Int微秒,因此您可能必须在循环中使用threadDelay,或者从delay使用unbounded-delays封装