我们使用 TPL 将长时间运行的任务排队到线程池中。有些任务可能会阻塞一段时间,所以我们使用以下模式来取消它们:
private void RunAction(Action action, CancellationTokenSourceWithException cts)
{
try
{
s_logger.Info("Starting action on thread ID: {0}", Utils.GetCurrentNativeThreadId());
Thread taskThread = Thread.CurrentThread;
cts.Token.Register(() => InterruptTask(taskThread));
s_logger.Info("Running next action");
action();
}
catch (Exception e)
{
cts.Cancel(e);
throw;
}
这样,调用cts.Cancel()
将导致任务线程被中断,以防它被阻塞。然而,这导致了一个问题:我们不知道线程是否真的得到了 ThreadInterruptedException。有可能我们调用Thread.Interrupt()
它,但线程将运行到完成,任务将简单地结束。在这种情况下,线程池线程将有一个 ThreadInterruptedException 形式的定时炸弹,当另一个任务在该线程上运行并试图阻塞时,它就会得到这个异常。
一种Thread.ResetInterrupted()
方法(类似于Thread.ResetAbort()
)在这里会有所帮助,但它似乎不存在。我们可以使用类似下面的东西:
try
{
someEvent.Wait(10);
}
catch (ThreadInterruptedException) {}
吞下ThreadInterruptedException,但它看起来很难看。
任何人都可以提出替代方案吗?我们在线程池线程上调用 Thread.Interrupt 是错误的吗?这似乎是取消任务的最简单方法:使用事件等的协作取消使用起来要麻烦得多,并且必须从任务传播到我们使用的所有类中。