您可以使用IORefs 在线程之间进行通信。 IORef 在分叉线程中的引用与在主线程中的引用相同。
您应该检查几件事:
- 分叉的线程真的有机会测试
IORef吗?
- 您所期望的 UI 交互真的可以从分叉线程中发生吗?许多 UI 库(包括
gtk 和 OpenGL)都对哪些线程可以与 UI 交互有限制。
- 标志设置的时间是否足够长,以至于分叉的线程有机会看到它?如果标志设置为
True,然后在派生线程调用readIORef 之前又回到False,它不会检测到停止。
解决最后一个问题的一种方法是使用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
线程周期性地做一些“工作”,这需要半秒钟,并在继续之前测试继续条件。
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 ()