再編集:StephenToubといくつかのメールを交換しました。以下では、元の回答と彼の回答を一貫性のある全体にマージしようとしています。
tl; drはこれを回避し、CompleteSynchronously
常にfalseを返すように強制します(非lectorに注意してください)。
.Net4.5の「重大な」変更に関するMSDNドキュメント
この質問を投稿した直後に、関連する質問をクリックして、「。NETFramework4.5のアプリケーションの互換性」に行き着きましたFromAsync
。
変更:IAsyncResult
実装は同期的に完了する必要があり
、結果のタスクを完了するには、そのCompletedSynchronously
プロパティがtrueを返す必要があります。
影響IAsyncResult
:実装が同期実行を完了しない
場合、結果のタスクは完了しませんが、そのCompletedSynchronously
プロパティはTrueを返します。
皮肉なことに、(または腹立たしいことに)CompletedSynchronously
州のページ:
実装者への注意:インターフェースのほとんどの実装者はIAsyncResult
このプロパティを使用しないため、falseを返す必要があります。
Stephen Toubは、これを次のように明確にしました。
http://msdn.microsoft.com/en-us/library/hh367887%28v=VS.110%29.aspx#coreの表
、特に「変更」の説明が間違っています(...) 。
.NET 4.5からに変更がありましたが、FromAsync
すべての
IAsyncResult.CompletedSynchronously
実装がtrueを返さなければならないというわけではありませんでした。それは意味がありません。変更点は、FromAsync
実際にIAsyncResult’s CompletedSynchronously
現在を確認することであり(.NET 4ではまったく確認しませんでした)、したがって、正確であることが期待されます。IAsyncResult
そのため、バグのある実装があった場合FromAsync
でも、.NET 4で機能していた可能性がありますが、.NET 4.5では、バグのある実装で機能する可能性は低くなります。
IAsyncResult.CompletedSynchronously
具体的には、を返し
ても大丈夫ですfalse
。ただし、が返される場合はtrue
、IAsyncResult
実際には同期的に完了している必要があります。CompletedSynchronously
返品
true
しても完了していない場合IAsyncResult
は、修正が必要なバグがあり、Task
返品元FromAsync
が正しく完了しない可能性があります。
パフォーマンス上の理由から変更が行われました。
問題のコードに戻る
これが彼の非常に役立つ分析です。これは、他の実装者にとっておそらく役立つので、完全に含めますIAsyncResult
。
問題は、使用しているライブラリの実装が非常に不完全であることにあるようですIAsyncResult
。特に、CompletedSynchronously
正しく実装されていません。それらの実装は次のとおりです。
public bool CompletedSynchronously
{
get { return _isCompleted; }
}
public bool IsCompleted
{
get { return _isCompleted; }
}
それらの_isCompleted
フィールドは、非同期操作が完了したかどうかを示します。これは問題あり
ません。このIsCompleted
プロパティは操作が完了したかどうかを示すためのものであるため、からこれを返すことは問題ありません。ただしCompletedSynchronously
、同じフィールドを返すことはできません。CompletedSynchronously
操作が同期的に完了したかどうか、つまり、への呼び出し中に完了したかどうかを返す必要があり、特定のインスタンス BeginXx
に対して常に同じ値を返す必要があります。IAsyncResult
使用方法の標準パターンを検討してください
IAsyncResult.CompletedSynchronously
。その目的は、の呼び出し元がBeginXx
、作業のためにコールバックを行うのではなく、後続の作業を続行できるようにすることです。これは、スタックダイブを回避するために特に重要です(すべてが実際に同期的に完了した非同期操作の長いシーケンスを想像してください。コールバックがすべての作業を処理した場合、各コールバックは次の操作を開始し、そのコールバックは次の操作を開始しますが、それらは同期的に完了し、コールバックもBeginXx
メソッドの一部として同期的に呼び出されるため、各呼び出しは、オーバーフローする可能性があるまで、スタック上でますます深くなります):
IAsyncResult ar = BeginXx(…, delegate(IAsyncResult iar) =>
{
if (iar.CompletedSynchronously) return;
… // do the completion work, like calling EndXx and using its result
}, …);
if (ar.CompletedSynchronously)
{
… // do the completion work, like calling EndXx and using its result
}
呼び出し元とコールバックの両方が同じ
CompletedSynchronously
プロパティを使用して、どちらがコールバックを実行するかを決定することに注意してください。そのCompletedSynchronously
ため、この特定のインスタンスに対して常に同じ値を返す必要があります。そうしないと、誤った動作が簡単に発生する可能性があります。たとえば、それらの実装は
。CompletedSynchronously
に相当するものを返しIsCompleted
ます。したがって、次の一連のイベントを想像してみてください。
BeginXx
が呼び出され、非同期操作が開始されます
BeginXx
呼び出し元に戻ります。呼び出し元はCompletedSynchronously
、操作がまだ完了していないため、falseをチェックします。
- これで操作が完了し、コールバックが呼び出されます。コールバックはそれ
CompletedSynchronously
がtrueであると認識し、呼び出し元がそれを実行したと想定するため、後続の作業は実行しません。
- そして今、誰も実行しないか、コールバックを実行しません。
つまり、ライブラリにはバグがあります。trueを返すように変更
CompletedSynchronously
した場合、この問題はマスクされていますが、別の問題が発生している可能性があります。呼び出し元(この場合FromAsync
)が操作がすでに完了していると考えた場合、メソッドの呼び出しに進み、メソッドは次のEndXx
ようにブロックされます。非同期操作が完了したので、非同期操作を同期操作に変えました。CompletedSynchronously
常にtrueを返すのではなく、常にfalseを返すようにしたことがありますか?