4

非同期の ProducerConsumerCollection を作成しようとしています。そのために、この msdn ページ ( http://msdn.microsoft.com/en-us/library/hh873173.aspx (ページの下部)) を使用しています。

私は今、タイムアウトを追加しようとしています。これが私がしていることです:

    public async Task<T> TakeWithTimeout(int timeout)
    {
            Task<T> takeTask = this.Take();

            if (timeout <= 0 || takeTask == await Task.WhenAny(this.tasks.Take(), Task.Delay(timeout)))
            {
                return await takeTask;
            }
            else
            {
                // Timeout
                return default(T);
            }
        }
    }

このコードの問題は、タイムアウトの場合、Take() メソッドによって作成されたタスクがキャンセルされないことです。

このタスクは TaskCompletionSource によって「作成」されているため、キャンセルトークンを渡すことはできませんか?

では、それをキャンセルして、この Take with timeout を適切に実装するにはどうすればよいでしょうか?

ありがとう :)

4

2 に答える 2

6

cancel-safeasyncフレンドリーなプロデューサー/コンシューマー コレクションを作成することは簡単ではありません。あなたがする必要があるのは、パラメーターとして aTakeを受け入れるように変更することであり、キャンセルされたときに がキャンセルされるようにハンドラーを登録する必要があります。CancellationTokenTaskCompletionSource

BufferBlock<T>キャンセルサポートが組み込まれているを使用することを強くお勧めします。

TPL Dataflow を使用できない場合 (たとえば、PCL で作業している場合や、Dataflow でサポートされていないターゲット プラットフォームを使用している場合)、オープンソースのAsyncEx ライブラリ( や などAsyncProducerConsumerQueue)のプロデューサー/コンシューマー コレクションを使用できますAsyncCollection。これらは両方とも、ブログで簡単に説明しているAsyncLockとに基づいています (キャンセルの詳細には触れません)。この設計で生産者/消費者コレクションのキャンセルをサポートする背後にある鍵は、でキャンセルをサポートすることです。条件変数の型がキャンセルをサポートすると、コレクションも簡単にキャンセルをサポートします。AsyncConditionVariableAsyncConditionVariable.WaitAsync

于 2013-07-23T20:12:22.947 に答える
2

TaskCompletionSource からタスクをキャンセルする方法という質問に自分のソリューションを投稿するだけです。それが自分で必要だったからです。

これは特定のニーズに使用できると思いますが、特定のタイムアウト機能に関連付けられていないため、これは一般的な解決策です (またはそう願っています)。

これは拡張メソッドです。

    public static async Task WaitAsync<T>(this TaskCompletionSource<T> tcs, CancellationToken ctok)
    {

        CancellationTokenSource cts = null;
        CancellationTokenSource linkedCts = null;

        try {

            cts = new CancellationTokenSource();
            linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, ctok);

            var exitTok = linkedCts.Token;

            Func<Task> listenForCancelTaskFnc = async () => {
                await Task.Delay(-1, exitTok).ConfigureAwait(false); 
            };

            var cancelTask = listenForCancelTaskFnc();

            await Task.WhenAny(new Task[] { tcs.Task, cancelTask }).ConfigureAwait(false);

            cts.Cancel();

        } finally {

            if(linkedCts != null) linkedCts.Dispose();

        }

    }

使用法:

    async Task TestAsync(CancellationToken ctok) {

        var tcs = new TaskCompletionSource<bool>();

        if (somethingOrTheOther) {
            tcs.TrySetResult(true);
        }

        await tcs.WaitAsync(ctok);

    }

アイデアは、監視非同期タスクがキャンセルされるまで本質的に永遠に待機することです。これは、まだ満たされていない場合に「早期に終了」するために使用できますがTaskCompletionSource、キャンセル要求のためにとにかく終了する必要があります。

監視タスクは、 の最後にキャンセルされることが保証されてWaitAsyncWhenAnyます。TaskCompletionSourceが結果に満足して完了し、が呼び出されるWhenAny次の行まで一時的に監視スリーパー タスクをそのままにしておくか、または渡された の結合トークンまたは内部トークンであるで取り消されました。cts.Cancel()exitTokenctokcts.Token

とにかく、これが理にかなっているといいのですが、このコードに問題がある場合はお知らせください...

于 2016-02-25T18:43:39.590 に答える