我正在使用yesod-websockets
编写应用程序,每当收到“开始”消息时,我需要运行一个连续生成数据并将其发送到客户端的线程,直到客户端告诉它停止。制作人也可以自行停止制作数据。
当生产者停止时(无论它是否自行停止生产,或者客户端告诉它停止),它会返回主循环并等待接收另一条“开始”消息。
代码看起来像这样(下面的runnable minimal repro):
wsApp :: WebSocketsT Handler ()
wsApp =
forever $ do
msg <- receiveMsg
case msg of
StartMsg -> do
race_
(produceData)
(whileM ((/= StopMsg) <$> receiveMsg))
问题是如果produceData
自行停止,那么运行receiveMsg
的线程将被取消,这会导致websocket连接被关闭。
21 / Mar / 2018:08:21:06 +0000 [错误#yesod]来自Warp的异常:ConnectionClosed @(app-0.0.0-5bzI9Onrk2fFepGGsdocDz:Application src / Application.hs:122:15)
有没有办法取消正在侦听连接的线程,而不会终止连接?
这是一个最小的复制品:
wsApp :: WebSocketsT Handler ()
wsApp = forever $ do
race_
(receiveData :: WebSocketsT Handler Text)
(pure ())
$logDebug "Trying again"
第二个线程将完成,第一个线程将被取消,导致连接被终止。
答案 0 :(得分:0)
由于我找不到更优雅的解决方案,我最终使用IORef Bool
来同步websockets线程和生产者线程。
wsApp :: WebSocketsT Handler ()
wsApp = do
producing <- newIORef False
forever $ do
msg <- receiveMsg
case msg of
StartMsg -> do
whenM (not <$> readIORef producing) $ do
atomicWriteIORef producing True
void . async $ produceData producing
StopMsg -> atomicWriteIORef producing False
produceData :: IORef Bool -> WebSocketsT Handler ()
produceData producing =
whenM (readIORef producing) $
case produce of
Nothing -> atomicWriteIORef producing False
Just x -> sendMsg x >> produceData producing