如何在每个实现的异步中抛出取消异常?

时间:2017-09-01 13:50:02

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

我为每个实现定制并使用了自定义异步,如下所示:

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冒泡,我收到一个由于取消而被其中一个线程抛出的异常。我也尝试将令牌传递给异步扩展方法,但这似乎也没有用。有人可以就如何做到这一点提供指导吗?感谢。

1 个答案:

答案 0 :(得分:3)

要让异常冒泡,您需要将令牌传递给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
);