阻止直到按键或给定时间 [英] Block until keypress or given time of day

查看:82
本文介绍了阻止直到按键或给定时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在(1)按键或(2)以前输入的时间( hh:mm )格式中的某一个到达之前阻止。如果有问题,我正在使用Windows。这个 DOS汇编程序(它也可以在Windows上运行)通过类似的东西 batchman waittil 16:30 来自Windows控制台,但我想完全在Haskell中完成,(即没有使用该程序)。

How does one block until the earlier of (1) a keypress or (2) a previously input time of day in hh:mm format is reached. I am using Windows in case that matters. This DOS assembler program (which does run on Windows too) does what I want via something like batchman waittil 16:30 from the Windows console but I want to do it entirely in Haskell, (i.e. without making use of that program).

推荐答案

您可以启动两个线程:一个读取字符,另一个等待达到指定时间;他们都写入一个 MVar 表示完成。

You can start two threads: one reads a character, the other waits until the specified time is reached; they both write to a single MVar to signal completion.

这有点棘手,但主要是由于细节:我们想要在非缓冲和非回应模式下有 stdin ,这样一个按键可以在不打印任何东西的情况下停止等待,然后恢复原始状态。而且我们还需要在完成后终止两个线程,例如,一旦超时到期,我们就停止从 stdin 读取数据。另外,如果发生异常,我们需要确保正确清理事情。

This is a little tricky, but mostly due to the details: we want to have stdin in unbuffered and non-echoing mode so that a single keypress stops the waiting without printing anything, and then restore the original state afterwards; and we also need to kill both threads after either finishes, so that we, for example, stop reading from stdin once the timeout expires. Additionally, we need to ensure things are cleaned up properly if an exception occurs. bracket simplifies the clean-up logic here, but it's still pretty ugly:

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

即便如此,该解决方案仍然无法处理等待特定时间 - 只是等待一定的微秒数。为了将一天中的某个时间调整到睡眠时间的微秒数,请此前SO问题可能有所帮助。如果睡眠时间足够长,那么它们可能不适合 Int 微秒,因此您可能必须使用 threadDelay 在一个循环中,或 延迟 来自无限延迟包。

Even after all that, this solution still doesn't handle waiting until a specific time — just waiting a certain number of microseconds. For turning a time of day into a number of microseconds to sleep, this previous SO question may help. If the sleeps are sufficiently long, then they might not fit into an Int of microseconds, so you might have to use threadDelay in a loop, or delay from the unbounded-delays package.

这篇关于阻止直到按键或给定时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆