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

Cha*_*ari 1 f# asynchronous exception-handling

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

这个组件的执行是异步执行的,我使用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;;
Run Code Online (Sandbox Code Playgroud)

编辑1:我发现,指着我为使用的文档HandleProcessCorruptedStateExceptionsAttribute一起SecurityCriticalAttribute,或使用一个配置项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()
    }
Run Code Online (Sandbox Code Playgroud)

程序仍然挂起而不返回或投掷.

Tom*_*cek 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" }
Run Code Online (Sandbox Code Playgroud)

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

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

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 }
Run Code Online (Sandbox Code Playgroud)