sda*_*das 3 multithreading haskell client-server
我正在尝试使用Haskell创建一个简单的服务器.当客户端连接到服务器时,服务器会记录其地址.每n微秒,服务器发送一个广播.
这是服务器
data Server = Server {
sSocket :: Socket,
sPort :: Port,
sClients :: MVar [ClientAddress]
}
Run Code Online (Sandbox Code Playgroud)
(注意允许从多个线程使用客户端的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
Run Code Online (Sandbox Code Playgroud)
服务器使用其线程并分叉.分叉线程处理任何传入的客户端请求
handleClientRequest server client = do
clients <- takeMVar $ sClients server
putMVar (sClients server) (client : clients)
Run Code Online (Sandbox Code Playgroud)
并使用该updateClients功能发送广播
updateClients server frequency = do
putStrLn "1"
clients <- (takeMVar $ sClients server)
putStrLn "2"
putStrLn $ show $ length clients
threadDelay frequency
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是"2"永远不会打印到屏幕上.我相信这是因为takeMVar线路updateClients永远不会完成.
为什么会冻结?
你从一个空的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
Run Code Online (Sandbox Code Playgroud)
现在,MVar总是满的,除非它实际上被客户端修改.
当您使用MVar来保护关键部分时,考虑正常状态是有帮助的; 在这种情况下,它是一个客户列表.MVar应该为空的唯一时间是客户端实际修改状态,因此如果您可以使用modifyMVar并设置初始状态,那么您的代码应该是无死锁的.
此外,如果您可以使用withMVar而不是takeMVar/putMVar您应该这样做,因为如果出现异步异常,它会使MVar保持一致状态.