Почему Async.Start, по-видимому, распространяет неуловимые исключения? - PullRequest
3 голосов
/ 15 апреля 2019

Рассмотрим это консольное приложение:

let throwAsync = 
  async { failwith "" }

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

Приложение сразу после запуска вылетает. Это не имеет смысла для меня по двум причинам:

  1. AFAIK (например, в этом блоге ) исключения не должны всплывать через Async.Start (что делает try ... with бессмысленным, но оно есть для пункта 2)
  2. (на удивление) код броска окружен try ... with, но исключение не перехватывается (оно никогда не печатает "Failed", и снова приложение вылетает).

Что происходит?

Ответы [ 2 ]

4 голосов
/ 15 апреля 2019

Исключение выдается в потоке потоков, где выполняется асинхронный блок.

Так что да, это означает, что исключение не распространяется на поток, который запустил Async.Start, и блок try-with никогда не будет обработан.Но также это означает, что исключение теперь выдается в другом месте, и без обработки исключений это приведет к сбою вашего приложения.

Цитирование MSDN :

Необработанные исключения в потоках пула потоков завершают процесс.Из этого правила есть три исключения:

  • A System.Threading.ThreadAbortException выбрасывается в поток пула потоков, потому что вызван Thread.Abort.
  • A System.AppDomainUnloadedException выбрасывается в потокпоток пула, поскольку домен приложения выгружается.
  • общеязыковая среда выполнения или хост-процесс прерывает поток.

Для получения дополнительной информации см. Исключения в управляемых потоках .

1 голос
/ 16 апреля 2019

A 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!
...