首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >多线程与gtk2hs

多线程与gtk2hs
EN

Stack Overflow用户
提问于 2015-06-09 16:09:19
回答 2查看 157关注 0票数 2

我正在用反应性香蕉和gtk2hs编写一些需要从文件句柄读取的代码。我需要至少有两个线程(一个线程使用反应性香蕉读取键盘事件,另一个线程从文件句柄读取),因此目前我有如下代码:

代码语言:javascript
复制
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

具有以下主要职能:

代码语言:javascript
复制
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

我的活动网络设置如下:

代码语言:javascript
复制
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的最后一行更改为

代码语言:javascript
复制
forkIO mainGUI
return ()

当程序到达mainAxn的末尾时,程序立即返回。我试图通过以下方法来解决这个问题:

代码语言:javascript
复制
forkIO mainGUI 
forever $ return ()

但是GUI根本没有打开,我也不明白为什么。

怎么做才是对的?我遗漏了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-06-09 19:05:14

这里的基本问题是,在Haskell,一旦main退出,整个程序就会被拆除。解决方案只是让main线程保持打开。

代码语言:javascript
复制
done <- newEmptyMVar
forkOS (mainGUI >> putMVar done ())
takeMVar done

我还将forkIO替换为forkOS。GTK在Windows上使用(OS-)线程本地状态,因此作为防御性编程,最好确保mainGUI运行在绑定线程上,以防有一天您想支持Windows。

票数 3
EN

Stack Overflow用户

发布于 2015-06-26 12:56:38

丹尼尔·瓦格纳( Daniel )回答了我的问题,但我从#haskell IRC频道得到了一个更丰富的视角,我将在这里发布,供将来参考。

一个更好的解决方案是让主线程成为GUI线程,并在一个新线程中处理反应性香蕉事件网络,而不是跳过脱离GUI线程和让主线程休眠的尴尬循环。最后,我修改了我的main函数以包含以下内容:

代码语言:javascript
复制
keyChan <- newChan
_ <- forkIO $ watchKeys keyPress keyChan
_ <- win `on` keyPressEvent $
    eventKeyVal >>= liftIO . writeChan keyChan >> return True

其中watchKeys被定义为:

代码语言:javascript
复制
watchKeys :: EventSource KeyVal -> Chan KeyVal -> IO ()
watchKeys keyPress chan = forever $
    readChan chan >>= fire keyPress 

现在,我可以在一个地方处理postGUI(A)Sync问题,方法是定义:

代码语言:javascript
复制
reactimateSafe :: Frameworks t => Event t (IO ()) -> Moment t ()
reactimateSafe = reactimate . fmap postGUIAsync

并将reactimateSafe用于任何修改GTK对象的IO操作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30737397

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档