您可以使用IORef
s在線程之間進行通信。 IORef
在分叉線程中引用與主線程中相同的東西。
有一些事情你應該檢查:
- 是否分叉線程真正得到一個機會來測試
IORef
?
- 您所期望的UI交互能否真正從分叉線程發生?許多UI庫(包括
gtk
和OpenGL
)都對哪些線程可以與UI進行交互存在限制。
- 標誌是否設置了足夠長的時間以致分叉線程有機會看到它?如果在分叉線程調用
readIORef
之前將標誌設置爲True
,然後返回False
10,則它不會檢測到停止。
解決最終問題的一種方法是使用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()