Pipes.Concurrent: Sent signal is delivered one click later than expected

时间:2016-02-03 03:25:39

标签: haskell concurrency gtk3 haskell-pipes

I'm using Pipes.Concurrent to write a short GUI program with GTK. It's a minesweeper-esque game, so I'm constructing a grid of buttons.

I construct and connect my buttons with:

b <- buttonNewWithLabel (show $ adjacencies board ! i)
on b buttonActivated 
     $ void . atomically $ send output (ClickSignal i (mines board ! i))
return b

And I connect the pipe with:

(output, input)  <- spawn (latest (ClickSignal 0 False))

let run = do
        sig <- await
        case sig of
            ClickSignal i isMine ->
                if isMine
                then do
                    lift $ labelSetText info (show i ++ " -- LOSE!")
                else do 
                    lift $ labelSetText info (show i ++ " -- Ok")
                    run
            Empty -> do
                lift $ labelSetText info "Empty Case"
                run

void . forkIO $ do 
    runEffect $ fromInput input >-> run
    performGC

It runs almost as expected. But if I click on button 1, nothing happens. But if I press button 23, it will update the info label to "1..". If I click on another button, it will update to "23..", and so forth.

I suspect that either I'm failing to understand how concurrent pipes are meant to work on some level or lazy IO is doing something weird.

1 个答案:

答案 0 :(得分:3)

使用latest缓冲策略意味着缓冲区中总有一个值可供读取,因此await将始终立即返回;只要它有机会跑,send同样总能成功。我写的测试程序在ghci中运行正常,但是在编译时它永远不会让试图从控制台和send读取的线程运行;我得到了无穷无尽的0,直到我终止了该计划。

您的消费者run中没有任何会导致其暂停的内容,因此可能同样会使正在尝试sendoutput的线程挨饿。如果是这种情况,在每次调用threadDelay 100之前,run左右可能会有所帮助。

另一种可能性和更有效的方法是使用阻塞Consumer的缓冲区,直到有消息要作用。为了避免阻塞事件回调,您可以使用newest 1缓冲区策略,该策略始终与send成功,并覆盖当前值(如果已存在)。

如果这两者都没有帮助,那么我怀疑你正在使用的GUI框架的方式还有其他问题,但我不熟悉Haskell中的GTK绑定,所以我不能对此有很大帮助。