如何在Parallel.ForEach中使用Regex.EnumerateMatches的ref struct枚举器?
解决Regex源生成器ref struct枚举器无法并行的问题
问题背景
为追求性能使用了Regex源生成器,其EnumerateMatches(span)方法返回ref struct枚举器,无法用于Parallel.ForEach等并行构造。需填充一个含100个结构体的数组,数组结构为value[0] -> padding[1] -> value[2] -> padding[3] -> ...(共50个value槽、50个padding槽),其中padding槽的填充依赖前后两个value槽的位置信息,希望通过并行化提升填充效率。
解决方案
核心思路
ref struct枚举器无法跨线程传递,因此先将所有匹配结果预收集到普通非值类型集合中,再分两个阶段并行填充数组:
- 并行填充所有value槽:每个value槽仅依赖对应的匹配结果,互相无依赖,可安全并行。
- 并行填充所有padding槽:待所有value槽填充完成后,每个padding槽仅依赖其前后的value槽,互相无依赖,也可安全并行。
代码实现
// 1. 预收集所有匹配结果到List,避开ref struct枚举器的跨线程限制 SpanRegexSrc regex = SpanRegexSrc.CachedRegex; var matches = regex.EnumerateMatches(myString.AsSpan()).ToList(); // 2. 并行填充value槽(索引为0,2,4,...,98) Parallel.For(0, matches.Count, matchIndex => { int valueSlotIndex = 2 * matchIndex; FillValueSlot(matches[matchIndex], ref _array[valueSlotIndex]); }); // 3. 并行填充padding槽(索引为1,3,5,...,99) Parallel.For(1, _array.Length, 2, padSlotIndex => { FillPadSlot(_array, padSlotIndex); });
注意事项
- 数组中每个索引仅被单个线程操作,无线程安全问题。
- 若匹配结果与value槽的对应关系不同(比如每个匹配对应两个value槽),只需调整
valueSlotIndex的计算逻辑即可,核心流程不变。 - 针对100个元素的数组,并行带来的性能提升可能有限,但该方案可无缝扩展到更大规模的数组。
内容的提问来源于stack exchange,提问作者Shpendicus
相关产品推荐
相关产品推荐

