Обработка исключений из-за поврежденного состояния с помощью Async.Catch - PullRequest
1 голос
/ 01 сентября 2011

Мне нужно, чтобы мое приложение знало, когда базовый компонент, который выполняется динамически, вызывает исключение из-за поврежденного состояния процесса, чтобы я мог записать его в журнал и пометить компонент как плохой для повторной загрузки и сбоя моего процесса.

Выполнение этого компонента выполняется асинхронно, и я использую 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: Я нашел документацию, указывающую на то, что я должен использовать либо HandleProcessCorruptedStateExceptionsAttribute вместе с SecurityCriticalAttribute, либо использовать запись конфигурации legacyCorruptedState­­ExceptionsPolicy=true.Я не хочу использовать запись конфигурации, если это вообще возможно.

РЕДАКТИРОВАТЬ 2 : Основываясь на предложении в комментарии, я изменил привязку let для 'b' следующим образом:

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

Программа по-прежнему зависает, не возвращаясь и не выбрасывая.

Ответы [ 2 ]

5 голосов
/ 01 сентября 2011

Это сложная проблема - внутри обычной функции вы можете поймать ThreadAbortException и сделать что-то в реакции, но вы не можете справиться с этим, потому что он будет автоматически переброшен (и это в конечном итоге убьет поток).

В асинхронном рабочем процессе F # обрабатывается исключение, и асинхронная среда выполнения F # сохраняет его, чтобы он мог сообщить об этом через продолжение, но, прежде чем он получит возможность сделать это, исключение перебрасывается .NET и убиваетпоток (таким образом RunSynchronously зависает).

Проблема в том, что - чтобы сообщить об исключениях, асинхронный вызов F # должен сделать некоторый вызов.Вызов не может быть сделан в текущем потоке (который отменяется).Если вы ожидаете исключения, вы можете начать работу в пуле потоков и обработать его самостоятельно.(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 }
3 голосов
/ 01 сентября 2011

как вы можете прочитать здесь - ThreadAbortException является одним из тех особых исключений в CLR.Кажется, он довольно сильно ломает Asycn-Pattern, поэтому я думаю, в этом проблема.

Попробуйте с другим исключением и посмотрите, будет ли это работать (должно)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...