线程在MVar操作中无限期地被阻塞

时间:2015-06-17 02:14:13

标签: haskell concurrency network-programming

我在尝试使用多个MV时尝试调试问题,但没有运气。

我的代码使用两个MVar:一个用于存储服务器的当前状态,另一个用于向客户端线程传递网络事件和从客户端线程传递网络事件。但是,在多次连接和断开连接后,服务器会在新客户端连接时停止发送数据(可能是因为网络事件MVar因任何原因而被清空)并最终因错误而跳闸:*** Exception: thread blocked indefinitely in an MVar operation

我在过去几天尝试调试此问题时得出以下结论:

  1. 用于修改MVar的函数不会抛出异常
  2. 在客户端连接或连接然后断开连接
  3. 之前,不会发生此问题
  4. 问题似乎是随机发生的(有时几个客户端可以连接然后断开连接,有时会立即断开)
  5. 我已将问题隔离到三个文件中:

    1. https://github.com/Mattiemus/IMC-Server/blob/master/IMC.hssense
    2. 中会抛出异常
    3. https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Networking/Server.hs(在application handleClient内修改,cleanupClient
    4. https://github.com/Mattiemus/IMC-Server/blob/master/IMC/Utilities/Concurrency.hs(推送和弹出存储在MVar中的列表的功能)
    5. 我完全没有想法,因为我只使用modifyMVar和withMVar(所以它肯定永远不会完全留空) - 我唯一的假设是修改MVar时可能会抛出异常,但我认为这是极不可能。

      感谢任何帮助,这个问题一直困扰着我。

3 个答案:

答案 0 :(得分:4)

三天后它解决了:实际上与网络或并发代码无关,而且由于我在Netwire中错误地重新实现了Yampas dpSwitch而导致了这种情况。对于希望实现此功能的任何人,修正了下面的代码:

dpSwitch :: (Monoid e, Applicative m, Monad m, T.Traversable col) => (forall wire. a -> col wire -> col (b, wire))
     -> col (Wire s e m b c)
     -> Wire s e m (a, col c) (Event d)
     -> (col (Wire s e m b c) -> d -> Wire s e m a (col c))
     -> Wire s e m a (col c)
dpSwitch route wireCol switchEvtGen continuation = WGen $ gen wireCol switchEvtGen
where
    gen wires switchEvtGenWire _ (Left x) = return (Left mempty, WGen $ gen wires switchEvtGenWire)
    gen wires switchEvtGenWire ws (Right x) = do            
        let routings = route x wires
        wireSteps <- T.sequenceA (fmap (\(wireInput, wire) -> stepWire wire ws (Right wireInput)) routings)
        let wireOutputs = T.sequenceA (fmap fst wireSteps)
            steppedWires = fmap snd wireSteps
        case wireOutputs of
            Left wireInhibitedOutput -> return (Left wireInhibitedOutput, WGen $ gen steppedWires switchEvtGenWire)
            Right wireResultOutput -> do
                (event, steppedSwitchEvtGenWire) <- stepWire switchEvtGenWire ws (Right (x, wireResultOutput))
                case event of
                    Left eventInhibited -> return (Left eventInhibited, WGen $ gen steppedWires steppedSwitchEvtGenWire)
                    Right NoEvent -> return (wireOutputs, WGen $ gen steppedWires steppedSwitchEvtGenWire)
                    Right (Event e) -> return (wireOutputs, continuation steppedWires e)

答案 1 :(得分:4)

对于任何可能偶然发现这一点的人来说,一些额外的信息thread blocked indefinitely in an MVar operation并不是那么聪明。当包含对MVar的引用的每个线程试图读取(或写入)该位置,已经死亡或正在等待永久阻塞的另一个原语时,就会发生这种情况。例如,线程1正在尝试读取MVar a并等待线程2,该线程已死,也试图读取MVar a,或尝试读取只能写入的MVar b在主题1中。

下面的代码很高兴地永远挂起:

do
  a <- newEmptyMVar
  forkIO (readMVar a >>= putStrLn)
  putMVar a $ last $ repeat 0

答案 2 :(得分:2)

我想我看到了问题 - 它出现在Server.hs中。您有在withMVar呼叫中执行网络IO的操作。现在想象IO有效地永久阻止。你既没有得到强制替换var的异常,也没有正常完成操作并替换var,因此你会陷入困境。

通常,即使您可以,也不应在withMVar来电中执行任何重要操作。如果你做任何这样的操作,你需要确保你有效地保护他们超时等等,这样你就可以确保他们总是以这种或那种方式完成。

相关问题