我为每个实现定制并使用了自定义异步,如下所示:
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
冒泡,我收到一个由于取消而被其中一个线程抛出的异常。我也尝试将令牌传递给异步扩展方法,但这似乎也没有用。有人可以就如何做到这一点提供指导吗?感谢。
答案 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
);