我正在编写一些带有reactive-banana和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
次调用会写入TextView
内置的mainAxn
。我发现我需要使用-threaded
进行构建,以使事件网络正确捕获来自textIn
的文本和来自keyPress
的按键,这会导致问题,因为从gtk修改对象是不安全的包装同时进行。
目前,我的代码中散布了postGUIAsync
个调用,我发现使用postGUISync
会导致整个事情陷入僵局 - 我不确定原因。我认为这是因为我最终在运行postGUISync
的同一个线程内调用了mainGUI
。
似乎最好在自己的线程中运行所有GUI内容,并使用postGUI*
函数进行每次访问。但是,当我将mainAxn
的最后一行更改为
forkIO mainGUI
return ()
程序在mainAxn
结束时立即返回。我试图通过使用:
forkIO mainGUI
forever $ return ()
但是gtk GUI根本就不会打开,我不明白为什么。
这样做的正确方法是什么?我错过了什么?
答案 0 :(得分:3)
这里的基本问题是,在Haskell中,只要main
退出,整个程序就会被拆除。解决方案只是保持main
线程打开; e.g。
done <- newEmptyMVar
forkOS (mainGUI >> putMVar done ())
takeMVar done
我还将forkIO
替换为forkOS
。 GTK在Windows上使用(OS-)线程本地状态,因此作为防御性编程的问题,最好确保mainGUI
在绑定线程上运行,以防有一天您想要支持Windows。
答案 1 :(得分:1)
Daniel Wagner回答了我提出的问题,但我从#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操作