tel*_*win 2 c# task-parallel-library
我们使用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;
}
Run Code Online (Sandbox Code Playgroud)
这样,调用cts.Cancel()将导致任务线程被阻塞,以防它被阻塞.但是,这导致了一个问题:我们不知道线程是否实际上得到了ThreadInterruptedException.我们可能会调用Thread.Interrupt()它,但线程将运行完成,任务将结束.在这种情况下,线程池线程将以ThreadInterruptedException的形式出现一个滴答声炸弹,当另一个任务在此线程上运行并尝试阻塞时,它将获得此异常.
一个Thread.ResetInterrupted()方法(类似于Thread.ResetAbort())在这里会有所帮助,但它似乎并不存在.我们可以使用以下内容:
try
{
someEvent.Wait(10);
}
catch (ThreadInterruptedException) {}
Run Code Online (Sandbox Code Playgroud)
吞下ThreadInterruptedException,但看起来很难看.
有人可以建议替代方案吗?在线程池线程上调用Thread.Interrupt是错误的吗?这似乎是取消任务的最简单方法:使用事件等的协作取消使用起来要麻烦得多,并且必须传播到我们从任务中使用的所有类中.
你不能这样做,因为你不知道线程池的线程是否/何时在不运行你自己的代码时会阻塞!
除了你提到的问题,如果一个线程在没有运行你自己的代码的情况下决定阻止,那么ThreadInterruptException将无法处理,应用程序将立即终止.这是你无法使用try/ block/catchguard解决的问题,因为存在竞争条件:防护可能刚刚在Thread.Interrupt调用时完成,因此如果运行时决定在该点处拥有线程阻塞,则会发生崩溃.
所以使用Thread.Interrupt不是一个可行的选择,你肯定必须建立合作取消.
除此之外,你可能不应该首先使用线程池来完成这些任务(虽然没有足够的数据.引用文档(强调我的):
如果您的短任务需要后台处理,则托管线程池是利用多个线程的简便方法.
有几种情况适合创建和管理自己的线程而不是使用线程池线程:
- ...
- 您有任务导致线程长时间阻塞.线程池具有最大线程数,因此大量被阻塞的线程池线程可能会阻止任务启动.
- ...
因此,你可能要考虑使用自己的线程池(有一个明显非常有信誉的实施在这里).
| 归档时间: |
|
| 查看次数: |
1022 次 |
| 最近记录: |