7

Simon Marlow在 Haskell eXchange 2012 上做了一个高性能并发演讲。由于时间限制,他跳过了关于简单并发聊天服务器的部分。对省略的内容感到好奇,通过网络搜索在服务器应用程序和GitHub 上的实现中发现了类似的幻灯片

幻灯片 33读取

回话……

talk :: Server -> Handle -> IO ()
talk server@Server{..} handle = do
    hSetNewlineMode handle universalNewlineMode
    hSetBuffering handle LineBuffering
    readName
  where
    readName = do
      hPutStrLn handle "What is your name?"
      name <- hGetLine handle
      m <- checkAddClient server name handle
      case m of
         Nothing -> do
           hPrintf handle "The name %s is in use" name
           readName
         Just client -> do
           runClient server client
              `finally` removeClient server name

严格来说,我们应该堵住和之间的洞checkAddClientfinally见注释……)

早些时候,幻灯片 3提到了“笔记​​中的第 14 章”,我认为这是指他即将出版的书checkAddClient和之间的同步裂缝是什么finally,我们如何堵塞它?

上述实现使用mask来自 Control.Exception。如果这是解决方法,那么不合时宜的异常会破坏聚会的情况是什么?

...
readName = do
  hPutStrLn handle "What is your name?"
  name <- hGetLine handle
  if null name
     then readName
     else mask $ \restore -> do
            ok <- checkAddClient server name handle
            case ok of
              Nothing -> restore $ do
                 hPrintf handle
                    "The name %s is in use, please choose another\n" name
                 readName
              Just client ->
                 restore (runClient server client)
                   `finally` removeClient server name
4

2 回答 2

2

您要确保每个成功checkAddClient都与removeClient. finally底部的语句仅保证removeClientrunClient动作开始时运行。

但是,在代码可以接收异步异常的结束checkAddClient和开始之间有一个简短的窗口。runClient如果是这样,finally将没有机会注册该removeClient命令。这就是西蒙所说的同步破解。

解决方案是默认屏蔽异步异常,只允许它们出现在某些地方(即由 包裹的动作restore)。这封住了上述裂缝。

于 2013-01-27T01:53:25.967 回答
0

如果checkAddClient这样做会怎样:

checkAddClient server name handle = do
  addClient server name handle
  return undefined

在案例评估其参数之前不会触发异常,并且removeClient永远不会被调用。

但是,老实说,我不了解异步异常,所以这是对示例的疯狂猜测。

于 2013-01-27T04:27:15.817 回答