【问题标题】:Pipes.Concurrent: Sent signal is delivered one click later than expectedPipes.Concurrent:发送的信号比预期晚一键传递
【发布时间】:2016-02-03 03:25:39
【问题描述】:

我正在使用 Pipes.Concurrent 用 GTK 编写一个简短的 GUI 程序。这是一款类似于扫雷的游戏,所以我正在构建一个按钮网格。

我构建并连接我的按钮:

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

我将管道连接到:

(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

几乎按预期运行。但是如果我点击按钮 1,什么都不会发生。但是如果我按下按钮 23,它会将信息标签更新为“1..”。如果我单击另一个按钮,它将更新为“23..”,依此类推。

我怀疑要么我无法理解并发管道是如何在某个级别上工作的,要么是惰性 IO 正在做一些奇怪的事情。

【问题讨论】:

  • 你能添加一个小的、精简的、独立的代码版本吗?

标签: haskell concurrency gtk3 haskell-pipes


【解决方案1】:

使用latest 缓冲策略意味着缓冲区中始终有一个值可供读取,因此await 将始终立即返回;并且send 将同样总是成功,只要它有机会运行。我编写的一个测试程序在 ghci 中运行良好,但是在编译时它永远不会让试图从控制台和send 读取的线程运行;在我终止程序之前,我收到了源源不断的0

您的消费者run 中没有任何会导致它暂停的内容,因此它可能同样使尝试将send 转换为output 的线程处于饥饿状态。如果是这种情况,在每次调用 run 之前,一个小的 threadDelay 100 左右可能会有所帮助。

另一种可能性,也是一种更有效的可能性,是使用一个缓冲区来阻止Consumer,直到有消息要处理。为避免阻塞事件回调,您可以使用 newest 1 缓冲区策略,该策略始终会成功使用 send 并覆盖当前值(如果那里已经存在)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    相关资源
    最近更新 更多