4

たまたまnullになるネストされたAsyncを呼び出すときに、問題に遭遇しました。例外が発生しましたが、非同期ワークフローが提供する通常の例外処理方法ではキャッチできません。

以下は、問題を再現する簡単なテストです。

[<Test>]
let ``Nested async is null with try-with``() = 

    let g(): Async<unit> = Unchecked.defaultof<Async<unit>>

    let f = async {
            try
                do! g()
            with e ->  
                printf "%A" e
    }

    f |> Async.RunSynchronously |> ignore

次の例外が発生します。

System.NullReferenceException : Object reference not set to an instance of an object.
at Microsoft.FSharp.Control.AsyncBuilderImpl.bindA@714.Invoke(AsyncParams`1 args)
at <StartupCode$FSharp-Core>.$Control.loop@413-40(Trampoline this, FSharpFunc`2 action)
at Microsoft.FSharp.Control.Trampoline.ExecuteAction(FSharpFunc`2 firstAction)
at Microsoft.FSharp.Control.TrampolineHolder.Protect(FSharpFunc`2 firstAction)
at Microsoft.FSharp.Control.AsyncBuilderImpl.startAsync(CancellationToken cancellationToken,     FSharpFunc`2 cont, FSharpFunc`2 econt, FSharpFunc`2 ccont, FSharpAsync`1 p)
at Microsoft.FSharp.Control.CancellationTokenOps.starter@1121-1.Invoke(CancellationToken     cancellationToken, FSharpFunc`2 cont, FSharpFunc`2 econt, FSharpFunc`2 ccont, FSharpAsync`1 p)
at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously(CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously(FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
at Prioinfo.Urkund.DocCheck3.Core2.Tests.AsyncTests.Nested async is null with try-with() in SystemTests.fs: line 345 

この場合、例外をキャッチする必要があると本当に思いますか、それともこれは本当に期待される動作ですか? (記録には Visual Studio 2010 Sp1 を使用しています)

また、次のテスト ケースAsync.CatchAsync.StartWithContinuations示されているのと同じ問題が発生します。

[<Test>]
let ``Nested async is null with Async.Catch``() = 

    let g(): Async<unit> = Unchecked.defaultof<Async<unit>>

    let f = async {
                do! g()
            }

    f |> Async.Catch |> Async.RunSynchronously |> ignore


[<Test>]
let ``Nested async is null with StartWithContinuations``() = 

    let g(): Async<unit> = Unchecked.defaultof<Async<unit>>

    let f = async {
                do! g()
            }

    Async.StartWithContinuations(f
                                , fun _ -> ()
                                , fun e -> printfn "%A" e
                                , fun _ -> ())

ワークフロービルダーのバインドメソッド内で例外が発生したようで、結果として通常のエラー処理コードがバイパスされていると思います。ドキュメントや他の場所で、これが意図された動作であることを示唆するものを見つけられなかったため、非同期ワークフローの実装のバグのように見えます。

ほとんどの場合、回避するのはかなり簡単だと思うので、少なくとも私にとっては大きな問題ではありませんが、すべての例外をキャプチャできる非同期例外処理メカニズムを完全に信頼できないことを意味するため、少し不安です.

編集:

少し考えた後、kvbに同意します。Null非同期は通常のコードには実際には存在すべきではなく、おそらくすべきではないことを行う場合(Unchecked.defaultOfを使用するなど)、またはリフレクションを使用して値を生成する場合にのみ生成される可能性があります(私の場合、それは関連するモックフレームワークでした) . したがって、これは実際にはバグではなく、エッジ ケースです。

4

2 に答える 2

5

バグではないと思います。名前が示すように、 はUnchecked.defaultof<_>生成する値が有効であることを確認せず、適切な値としてAsync<unit>サポートしませんnull(たとえば、 を使用しようとするとメッセージが表示されますlet x : Async<unit> = null)。 Async.Catchなどは、非同期計算内でスローされた例外をキャッチすることを目的としています。コンパイラの背後に忍び込み、無効な非同期計算を作成することによって発生した例外ではありません。

于 2012-01-19T10:48:43.070 に答える
4

私はkvbに完全に同意します-を使用して値を初期化すると、値を使用Unchecked.defaultOfする動作が未定義になる可能性があるため、これをバグとして扱うことはできません。null実際には、型の値を取得するべきではないため、心配する必要はありませんAsync<'T>

さらに詳細を追加すると、翻訳は次のようになるため、例外を処理できません。

async.TryWith
  ( async.Bind ( Unchecked.defaultof<_>, 
                 fun v -> async { printfn "continued" } ), 
    fun e -> printfn "%A" e)

によって返されたワークフローが開始される前にBind、メソッドから例外がスローされます (ワークフローは を使用してラップされているため、を呼び出した後に発生しますが、ワークフローの実行外で発生します)。この種の例外 (ワークフローが正しく構築されていないために発生) を処理したい場合は、ワークフローを実行し、実行外でスローされた例外を処理するのバージョンを作成できます。BindRunSynchronouslyDelayTryWith

let TryWith(work, handler) = 
  Async.FromContinuations(fun (cont, econt, ccont) ->
    try
      async { let! res = work in cont res }
      |> Async.StartImmediate
    with e -> 
      async { let! res = handler e in cont res } 
      |> Async.StartImmediate )   

次に、次のような例外を処理できます。

let g(): Async<unit> = Unchecked.defaultof<Async<unit>> 
let f = 
    TryWith
      ( (async { do! g() }),
        (fun e -> async { printfn "error %A" e }))
f |> Async.RunSynchronously
于 2012-01-19T15:05:46.160 に答える