在使用Parallel.ForEach()
时,有没有办法在特定线程上强制执行Thread.Abort
?
我知道Thread.Abort()
是不推荐的。
我正在一个包含成千上万个实体的集合上运行Parallel.ForEach()
。
在某些情况下,循环处理的数据可以追溯到30年前。我们遇到过一些线程挂起的问题。当我们试图掌握这一点时,我们希望调用implement a fail safe。如果线程运行的时间超过x,强制杀死线程。
我不想使用取消令牌。
这将是丑陋的,但还没有找到另一种解决方案。是否有可能:
- 让每个线程打开一个定时器。将
Thread.CurrentThread
的引用传递给定时器 - 如果计时器超时,而处理尚未完成,则在该计时器上调用
Thread.Abort
- 如果需要,发出事件等待句柄信号,以允许下一位患者进行处理
private void ProcessEntity(ProcessParams param,
ConcurrentDictionary<long, string> entities)
{
var options = new ParallelOptions
{
MaxDegreeOfParallelism = 2
};
Parallel.ForEach(person, options, p =>
{
ProcessPerson(param, p);
});
}
internal void ProcessPerson(ProcessParams param, KeyValuePair<long, string> p)
{
try
{
//...
}
catch (Exception ex)
{
}
param.eventWaitHandle?.WaitOne();
}
1条答案
按热度按时间0yg35tkg1#
看起来
Parallel.ForEach
方法在其工作线程为aborted时没有弹性,并且行为不一致。其他时候它传播包含ThreadAbortException
的AggregateException
,其他时候它直接抛出ThreadAbortException
,并显示丑陋的堆栈跟踪its internals。同样的情况也发生在
ThreadInterruptException
s上。这些异常是Thread.Interrupt
方法的结果。引用最近的GitHub问题:请注意,绝大多数代码,包括核心库中的代码,并没有针对线程中断的使用进行强化。这些可能会导致异常出现在线程处于等待/睡眠/加入状态的几乎任何地方,这意味着几乎所有的阻塞操作,获取锁的时间等,并且大多数代码都没有编写以适应此类异步异常。
解决这个问题的一种方法是限制
Thread.Abort
只在特定代码区域的影响,不允许它逃逸到您无法控制的代码路径。从.NET 7开始,有一个新的APIControlledExecution.Run
可以用于此目的。此方法需要一个Action
和一个CancellationToken
。如果在操作运行时取消令牌,当前线程被中止。然后ThreadAbortException
被捕获并处理,中止是reset,以便线程可以存活,并传播可以正常处理的OperationCanceledException
。下面是它的使用方法:这是一个危险的API,Microsoft不建议在生产代码中使用它。它主要用于测试相关的代码。
显然.NET Framework不包含
ControlledExecution.Run
API,但手动实现它并不是很困难。您可以在这个答案中找到一个实现(RunAbortable
方法)。.NET Core和.NET 6不支持中止线程。在这些平台上,您可以尝试interrupting线程而不是中止它们。它并不总是有效和响应,也不是100%安全(正如前面提到的最近的GitHub问题所证明的那样)。您可以在同一个答案中找到
RunInterruptible
实现。它可以在所有.NET平台上运行,旧的和新的。