为什么我的MVar会冻结我的代码?

时间:2014-08-02 21:31:50

标签: multithreading haskell client-server

我正在尝试使用Haskell创建一个简单的服务器。当客户端连接到服务器时,服务器会记录其地址。服务器发送广播时每 n 微秒。

这是服务器

data Server = Server {
  sSocket :: Socket,
  sPort :: Port,
  sClients :: MVar [ClientAddress]
  }

(注意允许从多个线程使用客户端的MVar。)

这是创建服务器的方式

startServer port = withSocketsDo $ do
  socket  <- listenOn $ PortNumber $ fromIntegral port
  clients <- newEmptyMVar
  let server = Server socket port clients
  forkIO $ forever $ accept socket >>= forkIO . (handleClientRequest server)
  forever $ updateClients server 1000000

服务器使用其线程并分叉另一个。分叉线程处理任何传入的客户端请求

handleClientRequest server client = do
  clients <- takeMVar $ sClients server
  putMVar (sClients server) (client : clients)

并使用updateClients函数

发送广播
updateClients server frequency = do
  putStrLn "1"
  clients <- (takeMVar $ sClients server)
  putStrLn "2"
  putStrLn $ show $ length clients
  threadDelay frequency

我遇到的问题是&#34; 2&#34;永远不会打印到屏幕上。我相信这是因为takeMVar中的updateClients行永远不会完成。

为什么会冻结?

1 个答案:

答案 0 :(得分:5)

你从一个空的MVar开始,所以takeMVar永远阻止。尝试使用newMVar []代替newEmptyMVar,可能是这样的:

startServer port = withSocketsDo $ do
  socket  <- listenOn $ PortNumber $ fromIntegral port
  clients <- newMVar []
  let server = Server socket port clients
  forkIO $ forever $ accept socket >>= forkIO . (handleClientRequest server)
  forever $ updateClients server 1000000

现在,MVar总是满的,除非客户实际修改它。

当您使用MVar来保护关键部分时,考虑正常状态是有帮助的;在这种情况下,它是一个客户列表。 MVar应该为空的唯一时间是客户端实际修改状态时,所以如果你可以使用modifyMVar并设置初始状态,那么你的代码应该没有死锁。

此外,如果您可以使用withMVar而不是takeMVar/putMVar,则应该这样做,因为如果出现异步异常,它会使MVar保持一致状态。