2014-09-06 39 views
1

我正在寫一些類似音樂播放器的東西,並與播放進度條卡在一起。如何在執行循環過程時退出forkIO線程

在我的程序中,當單擊播放按鈕時,我使用forkIO來分叉控制進度條的線程。但是,分叉線程現在執行一個循環。當我停止當前歌曲或更改歌曲時,如何通知該線程終止。

我一直在嘗試使用IOREF VAR,例如

flag <- newIORef False

forkIO $ progressBarFunc flag

,並在功能progreeBarFunc它檢查標誌是否爲真,並決定退出循環與否。

但這不起作用。

更一般地說,當我使用forkIO來分叉線程時,我該如何告訴分叉線程停止?另外,如果我有一個IORef Var並將它傳遞給forkIO中的函數,那麼主線程和分叉線程共享相同的IORef Var還是分叉線程實際上擁有它的副本?

回答

3

您可以使用IORef s在線程之間進行通信。 IORef在分叉線程中引用與主線程中相同的東西。

有一些事情你應該檢查:

  • 是否分叉線程真正得到一個機會來測試IORef
  • 您所期望的UI交互能否真正從分叉線程發生?許多UI庫(包括gtkOpenGL)都對哪些線程可以與UI進行交互存在限制。
  • 標誌是否設置了足夠長的時間以致分叉線程有機會看到它?如果在分叉線程調用readIORef之前將標誌設置爲True,然後返回False10,則它不會檢測到停止。

解決最終問題的一種方法是使用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()