Multithreading 多线程和gtk2hs
我正在用反应式香蕉和gtk2hs编写一些代码,这些代码需要从文件句柄读取。我需要至少有两个线程(一个用于读取带有响应香蕉的键盘事件,另一个用于读取文件句柄),因此目前我有如下代码:Multithreading 多线程和gtk2hs,multithreading,haskell,gtk,reactive-banana,Multithreading,Haskell,Gtk,Reactive Banana,我正在用反应式香蕉和gtk2hs编写一些代码,这些代码需要从文件句柄读取。我需要至少有两个线程(一个用于读取带有响应香蕉的键盘事件,另一个用于读取文件句柄),因此目前我有如下代码: type EventSource a = (AddHandler a, a -> IO ()) fire :: EventSource a -> a -> IO () fire = snd watch :: EventSource ByteString -> Handle -> IO
type EventSource a = (AddHandler a, a -> IO ())
fire :: EventSource a -> a -> IO ()
fire = snd
watch :: EventSource ByteString -> Handle -> IO ()
watch textIn pty = forever $
hGetLine pty >>= fire textIn >> threadWaitRead pty
具有以下主要功能:
mainAxn :: IO ()
mainAxn = do
h <- openFile "foo" ReadMode
initGUI
win <- windowNew
txt <- textViewNew
containerAdd win txt
widgetShowAll win
(keyPress, textIn) <-
(,) <$> newAddHandler <*> newAddHandler
network <- setupNetwork keyPress textIn
actuate network
_ <- forkIO $ watch textIn h
_ <- win `on` keyPressEvent $
eventKeyVal >>= liftIO . fire keyPress >> return True
mainGUI
程序在到达mainAxn
的末尾时立即返回。我尝试使用以下方法解决此问题:
forkIO mainGUI
forever $ return ()
但是GTKGUI从来没有打开过,我不明白为什么
正确的方法是什么?我遗漏了什么?这里的基本问题是,在Haskell中,
main
一退出,整个程序就被拆除。解决方法就是保持main
线程打开;e、 g
done <- newEmptyMVar
forkOS (mainGUI >> putMVar done ())
takeMVar done
done>putMVar-done())
takeMVar完成
我还将
forkIO
替换为forkOS
。GTK在Windows上使用(OS-)线程本地状态,因此作为一种防御编程,最好确保mainGUI
在绑定线程上运行,以防有一天您想要支持Windows。Daniel Wagner回答了我的问题,但我从#haskell IRC频道得到了更多信息,我将在这里发布,以供将来参考
更好的解决方案是让主线程成为GUI线程,并在一个新的线程中处理反应性香蕉事件网络,而不是跳过分叉GUI线程和让主线程进入睡眠的尴尬循环。我最终修改了我的main
函数以包含以下内容:
keyChan <- newChan
_ <- forkIO $ watchKeys keyPress keyChan
_ <- win `on` keyPressEvent $
eventKeyVal >>= liftIO . writeChan keyChan >> return True
现在我可以在一个地方处理postGUI(A)Sync
问题,方法是定义:
reactimateSafe :: Frameworks t => Event t (IO ()) -> Moment t ()
reactimateSafe = reactimate . fmap postGUIAsync
对于任何修改GTK对象的IO操作,使用
reactimateSafe
forever$return()
都是一个坏主意——而是等待mainGUI
线程完成。e、 g.此外,您应该使用forkOS mainGUI
。请看原因。(这里是反应性香蕉的作者。)我认为这个问题与反应性香蕉无关。在我看来,您需要-threaded
,以便threadWaitRead
与GTK GUI同时运行;但不完全确定。@HeinrichApfelmus是的,它非常独立于反应性香蕉,我只是加入了这些信息来更好地解释我的用例。另外,这是你亲自回答的第二个问题——谢谢你这么积极!
keyChan <- newChan
_ <- forkIO $ watchKeys keyPress keyChan
_ <- win `on` keyPressEvent $
eventKeyVal >>= liftIO . writeChan keyChan >> return True
watchKeys :: EventSource KeyVal -> Chan KeyVal -> IO ()
watchKeys keyPress chan = forever $
readChan chan >>= fire keyPress
reactimateSafe :: Frameworks t => Event t (IO ()) -> Moment t ()
reactimateSafe = reactimate . fmap postGUIAsync