我正在使用 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 做了一些奇怪的事情。
最佳答案
使用latest
缓冲策略意味着缓冲区中始终有一个值可供读取,因此 await
总是会立即返回;和send
只要有机会运行,同样总会成功。我编写的一个测试程序在 ghci 中工作正常,但编译时它永远不会让试图从控制台读取的线程运行,并且 send
;我刚刚收到源源不断的0
直到我终止该程序。
您的消费者run
其中没有任何会导致其暂停的内容,因此它可能同样会导致尝试 send
的线程挨饿。至output
。如果是这样的话,小threadDelay 100
大约在每次调用 run
之前可能有帮助。
另一种可能性,也是一种更有效的可能性,是使用一个缓冲区来阻止 Consumer
直到有消息需要采取行动。为了避免阻止事件回调,您可以使用 newest 1
缓冲策略,这总是会成功 send
如果已有值,则覆盖当前值。
如果这些都没有帮助,那么我怀疑您正在使用的 GUI 框架的检测方式还存在其他问题,但我对 Haskell 中的 GTK 绑定(bind)不是很熟悉,所以我不能对此有很大帮助。
关于haskell - 管道.并发: Sent signal is delivered one click later than expected,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35167939/