【问题标题】:How to throw cancellation exception in an async for each implementation?如何在异步中为每个实现抛出取消异常?
【发布时间】:2017-09-01 13:50:02
【问题描述】:

我为每个实现定义了一个自定义异步,并按如下方式使用:

public static Task ForEachAsync<T>(this IEnumerable<T> source, int partitionCount, Func<T, Task> body)
{
    return Task.WhenAll(
        from partition in Partitioner.Create(source).GetPartitions(partitionCount)
        select Task.Run(async delegate
        {
            using (partition)
            {
                while (partition.MoveNext())
                {
                    await body(partition.Current).ConfigureAwait(false);
                }
            }
        })
    );
}

...

List<long> ids = new List...

await ids.ForEachAsync(8,
    async (id) =>
    {
        await myTask(id);
    }
);

这很好用,但现在我需要修改它以允许传入取消令牌。我尝试过像这样简单的方法:

List<long> ids = new List...

await ids.ForEachAsync(8,
    async (id) =>
    {
        myToken.ThrowIfCancellationRequested();
        await myTask(id);
    }
);

但这失败了。正如我所料,我收到的不是OperationCanceledException 冒泡,而是由于取消而由其中一个线程引发的异常。我也尝试过将令牌传递给异步扩展方法,但这似乎也不起作用。有人可以就如何做到这一点提供指导吗?谢谢。

【问题讨论】:

  • 您能否缩小您的问题范围,排除“效果很好”的部分,只关注有问题的部分,并进一步明确问题,即:预期什么以及您得到了什么?谢谢和问候,
  • 您在尝试将 CancellationToken 注入扩展方法时是否使用了Task.Run( Action , CancellationToken)
  • @AlexBell 效果很好的部分是我正在尝试修改的相关代码。但是,我将编辑我的帖子以使其更加明确。感谢您的意见。
  • @Fildor 绝对不是。我会试一试。
  • 你在哪里捕捉到异常?

标签: c# asynchronous async-await .net-core cancellationtokensource


【解决方案1】:

要让异常冒泡,您需要将令牌传递给Task.Run,它只需对您的代码进行少量修改。

public static Task ForEachAsync<T>(this IEnumerable<T> source, int partitionCount, Func<T, Task> body, CancellationToken token = default(CancellationToken))
{
    return Task.WhenAll(
        from partition in Partitioner.Create(source).GetPartitions(partitionCount)
        select Task.Run(async delegate
        {
            using (partition)
            {
                while (partition.MoveNext())
                {
                    await body(partition.Current).ConfigureAwait(false);
                }
            }
        }, token) //token passed in
    );
}

习惯了

await ids.ForEachAsync(8,
    async (id) =>
    {
        myToken.ThrowIfCancellationRequested();
        await myTask(id);
    },
    myToken //token passed in
);

【讨论】:

  • 传递给Task.Run 的令牌只会阻止Task.Run 启动任务。它不会取消它。
  • @PauloMorgado 如果使用传入的相同取消令牌引发了 OperationCanceledException,它还会导致任务进入 Canceled 状态,这正是 OP 正在寻找的。跨度>
  • 运行这个using (var cts = new CancellationTokenSource(100)) { await Task.Run(async ()=&gt; await Task.Delay(1000), cts.Token); }和这个using (var cts = new CancellationTokenSource(100)) { await Task.Run(async ()=&gt; await Task.Delay(1000, cts.Token), cts.Token); }并解释区别。
  • @PauloMorgado 这个区别不是我在说的,我说的是为什么this program 输出在一个版本中被取消而在另一个版本中出现故障。
猜你喜欢
  • 2018-08-27
  • 2017-08-13
  • 2019-11-27
  • 1970-01-01
  • 1970-01-01
  • 2015-06-03
  • 1970-01-01
  • 2013-03-14
  • 1970-01-01
相关资源
最近更新 更多