使用Async.Catch处理进程损坏的状态异常

时间:2011-08-31 20:11:27

标签: exception-handling asynchronous f#

我需要在我的应用程序中知道动态执行的基础组件何时会引发进程损坏的状态异常,以便我可以记录它,并将该组件标记为再次加载并使我的进程崩溃。

此组件的执行是异步执行的,我使用Async.Catch来处理该异常。我尝试了以下代码来测试Async.Catch的行为,在我看来Async.Catch是挂起的。这对我来说是一种不良影响,并且怀疑所有PCSE都会导致相同的行为。

任何人都知道如何摆脱这种情况?

let a = async {
    System.Threading.Thread.CurrentThread.Abort()
}

let b = async {
    let! m = Async.Catch a
    return match m with
            | Choice1Of2 p -> "hello"
            | Choice2Of2 e -> "Caught: " + e.ToString()
}

Async.RunSynchronously b;;

编辑1 :我找到了指示我使用HandleProcessCorruptedStateExceptionsAttributeSecurityCriticalAttribute或使用配置条目legacyCorruptedState­­ExceptionsPolicy=true的文档。如果可能的话,我不想使用配置条目。

编辑2 :根据评论中的建议,我修改了'b'的let绑定,如下所示:

let b = async {
        try
            let! m = Async.Catch a
            return "hello"
        with
            | :? System.Threading.ThreadAbortException as e -> return "Caught: " + e.ToString()
    }

程序仍然挂起而不返回或抛出。

2 个答案:

答案 0 :(得分:5)

这是一个棘手的问题 - 在正常的函数中,你可以捕获ThreadAbortException并做出反应,但你无法真正处理它,因为它会被自动重新抛出(它最终会杀死线程)

在F#异步工作流中,处理异常并且F#async运行时将其存储,以便它可以通过延续报告它,但在它有机会执行此操作之前,.NET会重新抛出该异常并且它会杀死该线程(因此RunSynchronously挂起)。

问题是 - 报告异常,F#async需要进行一些调用。无法在当前线程(正在取消)上进行调用。如果您期望异常,则可以在线程池中开始工作并自行处理。 (F#不能自动执行此操作,因为它会产生过多的开销)。

您可以使用以下帮助程序:

type Microsoft.FSharp.Control.Async with
  static member CatchAbort<'R>(f : unit -> 'R) : Async<'R> =
    async { let hndl = new AutoResetEvent(false)
            let result = ref (Choice3Of3())
            ThreadPool.QueueUserWorkItem(fun _ ->
              try 
                result := Choice1Of3 (f())
                hndl.Set() |> ignore
              with 
                | e -> 
                   // This handler runs correctly even for ThreadAbort
                   result := Choice2Of3 e 
                   hndl.Set() |> ignore) |> ignore
            let! _ = Async.AwaitWaitHandle(hndl) 
            match !result with
            | Choice1Of3(res) -> return res
            | Choice2Of3(exn) -> 
                // Wrap or rethrow the exception in some way
                return raise exn
            | Choice3Of3 _ -> return failwith "unexpected case" }

这将启动线程池线程上的指定函数(非异步)。函数完成或抛出后,它会将结果报告回原始线程,这可以恢复工作流程。

为了适应您的示例,这应该按预期运行:

let a = async {
    let! value = Async.CatchAbort(fun () ->
      System.Threading.Thread.CurrentThread.Abort()
      "not aborted")
    return value }

let b = async {
    try let! m = a
        printfn "ok"
    with e -> printfn "Caught: %A" e }

答案 1 :(得分:3)

正如您可以阅读here - ThreadAbortException是CLR中的特殊异常之一。它似乎很难打破Asycn-Pattern,所以我猜这就是问题所在。

尝试使用其他异常并查看是否可行(应该)。