我正在尝试使用 Haskell 构建并发且健壮的代码,建议我使用 safe-exceptions和 async图书馆。但是,我很难理解如何处理异步操作中引发的非 fatal error 。
例如,如果有一个简单的循环每n秒检查一次网络资源,那么使用cancel
函数来停止它是有意义的,这会导致在单独的线程中抛出一个 AsyncCancelled
异常。当然,由于网络故障或其他问题,线程内也有可能抛出 IOError 。根据异常的类型及其包含的数据,我想控制单独的线程是否忽略异常、执行某些操作、停止或在主线程中引发异常。
使用安全异常库,唯一能够执行此操作的函数是catchAsync 和其他类似函数,这些函数在文档中被标记为危险。除此之外,异步库中有 waitCatch
,但是当我尝试提取 IOError 时,
:fromException
函数总是返回 Nothing
{-# 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
,或者是否有其他方法来处理我尚未发现的此类情况?
最佳答案
请注意 Control.Exception.Safe.throwTo
wraps同步异常放入 AsyncExceptionWrapper
中,并且 IOError
是同步的。 (我不知道为什么这种包装是必要的,无论如何你都不应该异步抛出同步异常。)
要使代码正常工作,您应该捕获 AsyncExceptionWrapper
或使用 Control.Exception.throwTo
。但实际上我并不完全理解你想要做什么,很可能你把事情变得过于复杂了。
关于multithreading - 如何使用安全异常库捕获异步异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50581476/