如何在执行循环过程时退出forkIO线程

时间:2014-09-06 03:34:06

标签: multithreading haskell

我正在编写类似音乐播放器的内容,并且无法使用播放进度条。

在我的程序中单击播放按钮时,我使用forkIO来分叉控制进度条的线程。但是,分叉线程现在执行循环。当我停止当前歌曲或更改歌曲时,如何通知该线程终止。

我一直在尝试使用IORef Var,例如

flag <- newIORef False

forkIO $ progressBarFunc flag

并在函数progreeBarFunc中检查flag是否为true并决定是否退出循环。

但这不起作用。

更一般地说,当我使用forkIO来分叉线程时,如何告诉分叉线程停止?

另外,如果我有一个IORef Var并将它传递给forkIO中的函数,那么主线程和分叉线程是否共享相同的IORef Var,或者分叉线程实际上有它的副本?

1 个答案:

答案 0 :(得分:3)

您可以使用IORef在线程之间进行通信。 IORef在分叉线程中引用与在主线程中相同的内容。

您应该检查一些事项:

  • 分叉线程是否真的有机会测试IORef
  • 您期望的UI交互实际上是否可以从分叉线程发生?许多UI库(包括gtkOpenGL)都限制哪些线程可以与UI交互。
  • 标志设置的时间是否足以使分叉线程有机会看到它?如果该标志设置为True,然后在分叉线程调用False之前返回readIORef,则它不会检测到停止。

解决最终问题的一种方法是使用Integer代替Bool作为标记。

newFlag :: IO (IORef Integer)
newFlag = newIORef 0

标志的观察者在创建观察者时记住标志的值,并在变大时停止。当线程可以继续时(标志尚未引发),这将返回True

testFlag :: IORef Integer -> IO (IO Bool)
testFlag flag = do
    n <- readIORef flag
    return (fmap (<=n) (readIORef flag))

要升高标志,信号器会递增该值。

raiseFlag :: IORef Integer -> IO ()
raiseFlag ref = atomicModifyIORef ref (\x -> (x+1,()))

这个小示例程序演示了IORef与其他线程共享标志。它在给定输入"f"时分叉新线程,在给定输入"s"时发出线程停止信号,并在给定输入"q"时退出。

main = do
    flag <- newFlag
    let go = do
        command <- getLine
        case command of
            "f" -> do
                continue <- testFlag flag
                forkIO $ thread continue
                go
            "s" -> do
                raiseFlag flag
                go
            "q" -> do
                raiseFlag flag
                return ()
    go

线程会定期执行一些工作&#34;这需要半秒钟,并在继续之前测试继续条件。

thread :: IO Bool -> IO ()
thread continue = go
    where
        go = do
            me <- myThreadId
            putStrLn (show me ++ " Outputting")
            threadDelay 500000
            c <- continue
            if c then go else putStrLn (show me ++ " Stopping") >> return ()