我正在用反应性香蕉和gtk2hs编写一些需要从文件句柄读取的代码。我需要至少有两个线程(一个线程使用反应性香蕉读取键盘事件,另一个线程从文件句柄读取),因此目前我有如下代码:
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我的活动网络设置如下:
setupNetwork :: EventSource KeyVal -> EventSource ByteString -> IO EventNetwork
setupNetwork keyPress textIn = compile $ do
ePressed <- fromAddHandler $ addHandler keyPress
eText <- fromAddHandler $ addHandler textIn
reactimate $ print <$> (filterJust $ keyToChar <$> ePressed)
reactimate $ print <$> eText(除了在我的实际代码中,这些reactimate调用写到构建在mainAxn中的TextView )。我发现我需要使用-threaded进行构建,以使事件网络能够正确地捕获来自textIn的文本和来自keyPress的按键,这会导致问题,因为并发地修改gtk包中的对象是不安全的。
目前,我将postGUIAsync调用分散在我的代码中,并且我发现使用postGUISync会导致整个事件陷入僵局--我不知道为什么。我认为这是因为我最终在运行mainGUI的线程中调用了mainGUI。
似乎最好在自己的线程中运行所有GUI内容,并为每次访问它使用postGUI*函数。但是,当我将mainAxn的最后一行更改为
forkIO mainGUI
return ()当程序到达mainAxn的末尾时,程序立即返回。我试图通过以下方法来解决这个问题:
forkIO mainGUI
forever $ return ()但是GUI根本没有打开,我也不明白为什么。
怎么做才是对的?我遗漏了什么?
发布于 2015-06-09 19:05:14
这里的基本问题是,在Haskell,一旦main退出,整个程序就会被拆除。解决方案只是让main线程保持打开。
done <- newEmptyMVar
forkOS (mainGUI >> putMVar done ())
takeMVar done我还将forkIO替换为forkOS。GTK在Windows上使用(OS-)线程本地状态,因此作为防御性编程,最好确保mainGUI运行在绑定线程上,以防有一天您想支持Windows。
发布于 2015-06-26 12:56:38
丹尼尔·瓦格纳( Daniel )回答了我的问题,但我从#haskell IRC频道得到了一个更丰富的视角,我将在这里发布,供将来参考。
一个更好的解决方案是让主线程成为GUI线程,并在一个新线程中处理反应性香蕉事件网络,而不是跳过脱离GUI线程和让主线程休眠的尴尬循环。最后,我修改了我的main函数以包含以下内容:
keyChan <- newChan
_ <- forkIO $ watchKeys keyPress keyChan
_ <- win `on` keyPressEvent $
eventKeyVal >>= liftIO . writeChan keyChan >> return True其中watchKeys被定义为:
watchKeys :: EventSource KeyVal -> Chan KeyVal -> IO ()
watchKeys keyPress chan = forever $
readChan chan >>= fire keyPress 现在,我可以在一个地方处理postGUI(A)Sync问题,方法是定义:
reactimateSafe :: Frameworks t => Event t (IO ()) -> Moment t ()
reactimateSafe = reactimate . fmap postGUIAsync并将reactimateSafe用于任何修改GTK对象的IO操作。
https://stackoverflow.com/questions/30737397
复制相似问题