不幸的是,可能没有更好的选择。这实际上取决于您的特定情况。这个想法是在安全点优雅地停止线程。这就是为什么
Thread.Abort不好的原因所在。因为它不能保证在安全点发生。通过在代码上添加停止机制,可以有效地手动定义安全点。这称为合作取消。基本上有4种广泛的机制可以做到这一点。您可以选择最适合您的情况。
轮询停止标志
您已经提到了此方法。这是很普通的一个。定期检查算法中安全点处的标志,并在收到信号时纾困。标准方法是标记变量
volatile。如果这不可能或不方便,则可以使用
lock。请记住,您不能标记局部变量,
volatile因此,例如,如果lambda表达式是通过闭包捕获的,那么您将不得不采用另一种方法来创建所需的内存屏障。此方法不需要说很多其他内容。
在TPL中使用新的取消机制
这类似于轮询停止标志,除了它使用TPL中的新取消数据结构。它仍然基于协作取消模式。您需要获取
CancellationToken和定期检查
IsCancellationRequested。要请求取消,你会打电话
Cancel对
CancellationTokenSource最初提供的令牌。您可以使用新的取消机制做很多事情。您可以在此处了解更多信息。
使用等待句柄
如果您的工作线程需要在特定间隔内等待或在其正常操作期间等待信号,则此方法很有用。你可以
Set一个
ManualResetEvent,例如,让线程知道是时候停下来。您可以使用该
WaitOne函数测试事件,该函数返回一个
bool指示事件是否已发出信号的指示。该
WaitOne有一个参数,指定了多少时间等待调用返回如果事件没有在该时间量信号。您可以使用此技术代替
Thread.Sleep并同时获得停止指示。如果
WaitHandle线程可能还要等待其他实例,这也很有用。您可以一次呼叫
WaitHandle.WaitAny以等待任何事件(包括停止事件)。使用事件比打电话更好
Thread.Interrupt因为您可以更好地控制程序的流程(
Thread.Interrupt抛出异常,所以您必须策略性地放置
try-catch块以执行任何必要的清除)。
特殊场景
有几种一次性方案具有非常专门的停止机制。枚举所有这些绝对超出了此答案的范围(没关系,这几乎是不可能的)。我的意思很好的例子是
Socket课堂。如果线程被阻塞在通话过程中
Send或
Receive则调用
Close将中断对任何阻塞调用它是在有效地疏通它的插座。我确信BCL中还有其他几个类似的技术可以用来解除线程阻塞的区域。
通过中断线程Thread.Interrupt
这样做的好处是它很简单,您不必专注于向代码中撒一些东西。缺点是您几乎无法控制算法中安全点的位置。原因是因为
Thread.Interrupt通过在一个固定的BCL阻止调用中注入异常来起作用。这些措施包括
Thread.Sleep,
WaitHandle.WaitOne,
Thread.Join等,所以你必须要聪明在哪里放置它们。但是,在大多数情况下,算法会指示它们的去向,而通常情况下这还是很好的,尤其是如果您的算法将大部分时间都用在了这些阻塞调用之一中时。如果您的算法未在BCL中使用阻塞调用之一,则此方法将对您不起作用。这里的理论
ThreadInterruptException是只能从.NET等待调用生成,因此很
可能
在安全点。至少您知道该线程不能处于非托管代码中或无法脱离关键部分,而使悬挂的锁处于获取状态。尽管这种方法具有较小的侵入性,但
Thread.Abort我仍然不鼓励使用它,因为尚不清楚哪个调用能够响应它,并且许多开发人员将不熟悉它的细微差别。



