1

メソッドを考えてみましょう:

Task Foo(IEnumerable items, CancellationToken token)
{
    return Task.Run(() =>
    {
        foreach (var i in items)
            token.ThrowIfCancellationRequested();

    }, token);
}

それから私は消費者を持っています:

var cts = new CancellationTokenSource();
var task = Foo(Items, cts.token);
task.Wait();

アイテムの例:

IEnumerable Items
{
    get
    {
        yield return 0;
        Task.Delay(Timeout.InfiniteTimeSpan).Wait();
        yield return 1;
    }
}

task.Wait はどうですか? アイテムのコレクションにキャンセル トークンを入れることができません

応答しないタスクを強制終了する方法、またはこれを回避する方法は?

4

7 に答える 7

3

第三者から発信されたアイテムにキャンセルトークンを入れることを可能にする1つの解決策を見つけました:

public static IEnumerable<T> ToCancellable<T>(this IEnumerable<T> @this, CancellationToken token)
{
    var enumerator = @this.GetEnumerator();

    for (; ; )
    {
        var task = Task.Run(() => enumerator.MoveNext(), token);
        task.Wait(token);

        if (!task.Result)
            yield break;

        yield return enumerator.Current;
    }
}

今私は使用する必要があります:

Items.ToCancellable(cts.token)

そして、キャンセル要求後にハングしません。

于 2013-08-07T13:42:17.187 に答える
2

キャンセル不可能な操作を実際にキャンセルすることはできません。Stephen Toub はParallel FX チームのブログの「キャンセルできない非同期操作をキャンセルするにはどうすればよいですか?」で詳しく説明していますが、本質的には、実際に何をしたいのかを理解する必要があるということです。

  1. 非同期/長時間実行オペレーション自体を停止しますか? 操作を知らせる方法がない場合、協力的な方法では実行できません
  2. 結果を無視して、操作が完了するのを待つのをやめますか? それは実行可能ですが、明らかな理由で信頼性が低下する可能性があります。キャンセル トークンを渡す長い操作で Task を開始するか、Stephen Toub が説明するように TaskCompletionSource を使用できます。

適切な解決策を見つけたい行動を決める必要があります

于 2013-08-07T14:35:44.467 に答える
1

IEnumerable<Task<int>>の代わりにアイテムを使用できますIEnumerable<int>か? それからあなたはすることができます

return Task.Run(() =>
{
    foreach (var task in tasks)
    {
        task.Wait(token);
        token.ThrowIfCancellationRequested();
        var i = task.Result;
    }
}, token);

このようなことは、Reactive Framework を使用して実行する方が簡単かもしれませんがitems.ToObservable。それは次のようになります。

static Task<int> Foo(IEnumerable<int> items, CancellationToken token)
{
    var sum = 0;
    var tcs = new TaskCompletionSource<int>();
    var obs = items.ToObservable(ThreadPoolScheduler.Instance);
    token.Register(() => tcs.TrySetCanceled());
    obs.Subscribe(i => sum += i, tcs.SetException, () => tcs.TrySetResult(sum), token);
    return tcs.Task;
}
于 2013-08-07T13:15:14.557 に答える
0

それ自体がアイテム間でキャンセル可能な enumerable の周りにラッパーを作成するのはどうですか?

IEnumerable<T> CancellableEnum<T>(IEnumerable<T> items, CancellationToken ct) {
    foreach (var item in items) {
        ct.ThrowIfCancellationRequested();
        yield return item;
    }
}

...しかし、それは Foo() がすでに行っていることのようなものです。この列挙可能なブロックが文字通り無限にブロックされる場所がある場合 (そして、非常に遅いだけではありません)、消費者側の task.Wait() にタイムアウトまたはキャンセル トークンを追加します。

于 2013-08-07T12:42:47.753 に答える