执行Regex时CancellationTokenSource失效问题求助
问题分析与解决方案
你猜的没错!你的情况和那个主题里的问题本质完全一致——你的代码(包括深层的Regex逻辑)根本没有监听CancellationToken的取消请求,所以哪怕你调用了CancelAfter,也没法打断那个陷入死循环的Regex匹配过程。
先拆解下你两种实现里的具体问题:
第一种实现的核心问题
- 你用了
task.RunSynchronously(),这会直接阻塞当前线程同步执行任务,后面的task.Wait(1000)完全是多余的——要么任务已经跑完,要么已经卡在Regex死循环里了,等待超时后调用Cancel()也毫无意义,因为任务里根本没检查令牌。 - 日志部分的异步方法没加
await,会导致文件读写冲突,日志内容也可能乱序或丢失。
第二种实现的核心问题
- 虽然用了
await Task.Run(..., token),但TryToCreateCodeFile以及更深层的Regex.Matches()完全没理会这个令牌。TaskCanceledException只会在任务主动检查并响应令牌时才会抛出,否则任务会一直卡在死循环里,永远不会触发异常。 - 同样,日志的异步方法没加
await,存在资源竞争问题。
可行的解决方案
短期应急:强制终止超时任务(有风险)
因为Regex.Matches()的死循环是在托管线程内部阻塞,没法通过常规的令牌检查中断,所以我们可以用Task.WhenAny结合线程中断的方式强制终止(注意:这种方式比较粗暴,可能导致资源泄漏,仅作为临时方案):
public static async Task<CodeFile> CreateCodeFile(string fileName) { var fileInfo = new FileInfo(fileName); foreach (var codeFileFactory in CodeFileFactories) { using var cts = new CancellationTokenSource(); cts.CancelAfter(1000); Task<CodeFile> task = null; Thread taskThread = null; // 启动任务并记录执行它的线程 task = Task.Run(() => { taskThread = Thread.CurrentThread; return TryToCreateCodeFile(codeFileFactory, fileInfo); }, cts.Token); // 等待任务完成或超时 var completedTask = await Task.WhenAny(task, Task.Delay(Timeout.Infinite, cts.Token)); if (completedTask == task) { // 任务正常完成,返回结果 var result = await task; if (result != null) return result; } else { // 超时,尝试中断执行线程 if (taskThread != null && taskThread.IsAlive) { try { taskThread.Interrupt(); } catch (ThreadStateException) { // 线程已结束,忽略异常 } } // 修正日志写法:必须await异步方法 var existingLog = await File.ReadAllTextAsync("FailedLog.txt"); await File.WriteAllTextAsync("FailedLog.txt", $"{existingLog}\r\n{fileName}"); } } return null; }
长期根治:修复正则表达式
线程中断只是权宜之计,最稳妥的方式还是优化你的正则表达式,消除灾难性回溯导致的死循环:
- 避免嵌套的贪婪量词(比如
(a+)+) - 给正则添加明确的边界条件,减少匹配时的回溯次数
- 可以用
RegexOptions.Compiled提前编译正则,同时在测试时用Regex.Matches的超时重载(.NET 5+支持):// .NET 5+ 支持的带超时的Matches方法 var matches = regex.Matches(content, matchTimeout: TimeSpan.FromSeconds(1));
内容的提问来源于stack exchange,提问作者cheny
相关产品推荐
相关产品推荐

