今天凌晨,用long-polling做聊天功能,服务器端直接用TChan广播:
get "/recv" $ do
msg <- liftIO $ atomically $
c <- dupTChan chan
readTChan c
json (msg::Message)
我想当然的在一个atomically里用dupTChan复制一个全局的TChan,然后用readTChan读数据,最后把结果转成json返回给客户端。因为readTChan在TChan没数据的时候会阻塞直到有数据,所以这里就实现了long-polling。
但是,测试的时候发现即使有向全局的chan写入数据,请求/recv还是会一直阻塞,检查一遍JS之后没发现问题,再看TChan的文档才发现问题所在。
dupTChan复制得到的TChan是空的,所以后面紧跟的readTChan一定会通过retry阻塞。retry的时候会重新执行事务,dupTChan又得到一个空TChan,然后就死循环了。Haskell的retry又挺厉害,不会真的在这里死循环,CPU使用也没太大变化,所以很难发现。
所以这里要分成两个事务:
get "/recv" $ do
c <- liftIO $ atomically $ dupTChan chan
msg <- liftIO $ atomically $ readTChan c
json (msg::Message)