使用Partitioner的ForEachAsync抛出ArgumentOutOfRangeException异常排查
解决ForEachAsync偶发System.ArgumentOutOfRangeException(partitionCount参数)问题
刚碰到过类似的坑,结合你描述的场景,咱们一步步理清楚:
问题场景复盘
服务器上跑了20个多线程应用,只有2个在新增多线程逻辑后,调用自定义AsyncHelper.ForEachAsync时偶发System.ArgumentOutOfRangeException,错误直接指向partitionCount参数。一开始大家都怀疑是并行度过高导致的,但排查后发现空列表才是真正的诱因——其他应用的ForEachAsync调用没碰到空集合场景,所以没触发这个问题。
常见的AsyncHelper.ForEachAsync实现(参考你提供的代码逻辑)
通常这类自定义实现会是这样的:
public static async Task ForEachAsync<T>(this IEnumerable<T> source, int partitionCount, Func<T, Task> body) { var partitions = Partitioner.Create(source).GetPartitions(partitionCount); using (partitions) { await Task.WhenAll( from partition in partitions select Task.Run(async () => { using (partition) { while (partition.MoveNext()) { await body(partition.Current); } } })); } }
问题根源
当传入的source是空列表时,Partitioner.Create(source).GetPartitions(partitionCount)尝试创建指定数量的分区,但对于空集合,分区器内部逻辑无法生成有效分区,此时如果partitionCount大于0(你调用时肯定传的是正数),就会触发参数越界异常——这个场景不是每次都出现,可能和线程调度、分区器内部的偶发逻辑有关,所以才是"偶发"。
解决方案
给你两个靠谱的解决思路:
思路1:调用前提前拦截空集合
在调用ForEachAsync之前先判断集合是否为空,为空直接跳过,避免进入有问题的逻辑:
var targetDataList = GetYourDataList(); // 空集合直接返回,不执行异步遍历 if (!targetDataList.Any()) { return; } await AsyncHelper.ForEachAsync(targetDataList, 4, async item => { // 你的业务处理逻辑 });
思路2:修改AsyncHelper的实现,内部处理空集合边界
在自定义的ForEachAsync方法里增加空集合判断,同时也可以加固partitionCount的合法性校验:
public static async Task ForEachAsync<T>(this IEnumerable<T> source, int partitionCount, Func<T, Task> body) { // 先做参数合法性校验 if (source == null) throw new ArgumentNullException(nameof(source)); if (partitionCount <= 0) throw new ArgumentOutOfRangeException(nameof(partitionCount), "并行度必须大于0"); // 空集合直接返回,无需后续处理 if (!source.Any()) { return; } var partitions = Partitioner.Create(source).GetPartitions(partitionCount); using (partitions) { await Task.WhenAll( from partition in partitions select Task.Run(async () => { using (partition) { while (partition.MoveNext()) { await body(partition.Current); } } })); } }
补充说明
为什么只有这两个应用出问题?因为其他应用的业务逻辑里,传给ForEachAsync的集合从来不会为空,而这两个应用新增多线程逻辑后,出现了处理空数据的场景,才触发了这个隐藏的边界问题。
内容的提问来源于stack exchange,提问作者Gun
相关产品推荐
相关产品推荐

