为什么Async.Start似乎传播了不可捕获的异常?

时间:2019-04-15 16:52:10

标签: asynchronous exception f# async-await

考虑此控制台应用程序:

let throwAsync = 
  async { failwith "" }

[<EntryPoint>]
let main argv =
  try Async.Start throwAsync
  with _ -> printfn "Failed"
  System.Console.Read() |> ignore
  0

该应用在运行时立即崩溃。这对我来说没有任何意义,原因有两个:

  1. AFAIK(例如this blog post)异常不应通过Async.Start冒出(使try ... with毫无意义,但在第二点就可以了)
  2. (令人惊讶的)引发代码被try ... with包围,但是没有捕获到异常(它永远不会打印"Failed",并且应用程序再次崩溃)。

这是怎么回事?

2 个答案:

答案 0 :(得分:4)

在执行异步块的线程池线程上引发异常。

是的,这意味着该异常不会传播到运行Async.Start的线程,并且永远不会命中try-with块。但是,这也意味着该异常现在已被抛出到其他地方,并且没有任何异常处理将导致您的应用程序崩溃。

引用MSDN

  

线程池线程中未处理的异常会终止进程。此规则有三个例外:

     
      
  • 因为调用了System.Threading.ThreadAbortException,所以在线程池线程中抛出了Thread.Abort
  •   
  • 由于正在卸载应用程序域,因此在线程池线程中引发了System.AppDomainUnloadedException
  •   
  • 公共语言运行时或宿主进程终止线程。
  •   
     

有关更多信息,请参见Exceptions in Managed Threads

答案 1 :(得分:1)

try执行时,async无法捕获Async.Start中的异常,因为它们分叉在不同的线程中。如果您想捕获它,可以使用Async.RunSynchronously或在另一个async中捕获它:

let throwAsync = async { failwith "I was not caught!" }

let catchAsync = async {
    try 
        do! throwAsync
    with _-> printfn "caught inside async!"
}

[<EntryPoint>]
let main argv =
    try throwAsync |> Async.RunSynchronously 
    with _ -> printfn "caught outside!"
    try catchAsync |> Async.Start 
    with _ -> printfn "I did not catch it either!"
    System.Console.Read() |> ignore
    printfn "finishing!"
    0

输出:

caught outside!
caught inside async!
finishing!