首页 > 解决方案 > 如何使用安全异常库捕获异步异常?

问题描述

我正在尝试使用 Haskell 构建并发且健壮的代码,建议我使用安全异常异步库。但是,我很难理解如何处理动作中引发的非致命错误async

例如,如果有一个简单的循环每隔n秒检查一次网络资源,那么使用会导致在单独线程中引发异常的cancel函数来停止它是有意义的。AsyncCancelled当然,也有可能IOError由于网络中断或其他问题而从线程内抛出 an 。根据异常的类型及其包含的数据,我想控制单独的线程是否忽略异常、执行某些操作、停止或在主线程中引发异常。

使用安全异常库,唯一能够做到这一点的函数是catchAsync和其他类似的函数,它们在文档中被标记为危险。除此之外,waitCatch异步库中有,但是当我尝试提取时该fromException函数总是返回:NothingIOError

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Concurrent.Async
import Control.Concurrent hiding (throwTo)
import Control.Exception.Safe 
import Control.Monad
import System.IO
import System.IO.Error hiding (catchIOError)

main = do
    hSetBuffering stdin NoBuffering
    putStrLn "Press any key to continue..."
    a <- async runLoop
    async $ hWaitForInput stdin (-1) *> 
                throwTo (asyncThreadId a) (userError "error")
    waitCatch a >>= either handler nothing
  where
    printThenWait i = putStr (show i ++ " ") *> threadDelay 1000000
    runLoop = sequence_ $ printThenWait <$> [1..]
    nothing _ = pure ()
    handler e
        | Just (e' :: IOError)       <- fromException e = 
              putStrLn "It's an IOError!"
        | (Nothing :: Maybe IOError) <- fromException e = 
              putStrLn "We got Nothing!"

我对从异步异常中恢复的危险感到有些困惑,尤其是当标准函数(例如cancel导致它们被抛出)时,我不知道在使用这两个库时推荐的处理它们的方法是什么。这是一个值得推荐的例子,catchAsync 还是有另一种方法来处理我没有发现的这些类型的情况?

标签: multithreadinghaskellexception-handlingthread-exceptions

解决方案


请注意,将同步异常Control.Exception.Safe.throwTo 包装AsyncExceptionWrapper到, 并且IOError 同步的。(我不知道为什么这种包装是必要的,无论如何你都不应该异步抛出同步异常。)

要使您的代码正常工作,您应该捕获AsyncExceptionWrapper或使用Control.Exception.throwTo. 但实际上我并不完全理解你想要做什么,很可能你把事情复杂化了。


推荐阅读