特定のタスクまたはデリゲートの背後にあるソースを確認せずに、タスク/デリゲートの「制御されていない」キャンセルと制御/協調を区別できないことがわかりました。
OperationCanceledException
具体的には、「下位レベルの操作」からスローされたものをキャッチするときに、参照されたトークンが現在の操作のトークンと一致しない場合、それは失敗/エラーとして解釈されるべきであると常に想定してきました。これは、あきらめた(終了した)という「下位レベルの操作」からの声明ですが、あなたがそうするように頼んだからではありません。
残念ながら、キャンセルの理由としてTaskCompletionSource
a を関連付けることはできません。CancellationToken
そのため、組み込みのスケジューラによってサポートされていないタスクは、キャンセルの理由を伝えることができず、協調的なキャンセルをエラーとして誤って報告する可能性があります。
更新: .NET 4.6 の時点で、TaskCompletionSourceは、またはの新しいオーバーロードが使用されている場合に関連付けることができます。CancellationToken
SetCanceled
TrySetCanceled
たとえば、次の
public Task ShouldHaveBeenAsynchronous(Action userDelegate, CancellationToken ct)
{
var tcs = new TaskCompletionSource<object>();
try
{
userDelegate();
tcs.SetResult(null); // Indicate completion
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == ct)
tcs.SetCanceled(); // Need to pass ct here, but can't
else
tcs.SetException(ex);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}
private void OtherSide()
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
cts.Cancel();
Task wrappedOperation = ShouldHaveBeenAsynchronous(
() => { ct.ThrowIfCancellationRequested(); }, ct);
try
{
wrappedOperation.Wait();
}
catch (AggregateException aex)
{
foreach (var ex in aex.InnerExceptions
.OfType<OperationCanceledException>())
{
if (ex.CancellationToken == ct)
Console.WriteLine("OK: Normal Cancellation");
else
Console.WriteLine("ERROR: Unexpected cancellation");
}
}
}
すべてのコンポーネントに配布されたキャンセル トークンを介してキャンセルが要求された場合でも、"エラー: 予期しないキャンセル" が発生します。
主な問題は、TaskCompletionSource が CancellationToken を認識していないことですが、非同期操作を Tasks でラップするための「go to」メカニズムがこれを追跡できない場合、インターフェイス全体で追跡されることを期待できないと思います(ライブラリ) 境界。
実際、TaskCompletionSource はこれを処理できますが、必要な TrySetCanceled オーバーロードは内部であるため、mscorlib コンポーネントのみが使用できます。
キャンセルがタスクとデリゲートの境界を越えて「処理」されたことを伝えるパターンを持っている人はいますか?