2014-09-06 4 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, которые вы ожидаете, действительно происходят из разветвленной нити? Многие библиотеки пользовательского интерфейса, включая как gtk, так и OpenGL, имеют ограничения на то, какие потоки могут взаимодействовать с пользовательским интерфейсом.
  • Установлен ли флаг достаточно долго, чтобы разветвленная нить имела возможность увидеть его? Если флаг установлен на 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 

Нити периодически выполняют некоторую «работу», которая занимает полсекунды, и проверяйте состояние продолжения до продолжения.

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() 
Смежные вопросы