一尘不染

有关在.NET中干净终止线程的问题

c#

我从阅读过很多关于该主题的文章中了解到Thread.Abort()是邪恶的,所以我目前正在淘汰中止程序,以更干净的方式替换它。然后在这里比较了人们在stackoverflow上的用户策略,然后阅读了MSDN的
如何:创建和终止线程(C#编程指南) ”后,两者都指出了一种非常相似的方法-即使用一种volatile bool方法检查策略,很好,但是我还有几个问题。

立即对我来说突出的是,如果您没有一个仅运行循环代码的简单工作进程,该怎么办?例如,对于我来说,我的过程是一个后台文件上传器过程,实际上我确实遍历每个文件,因此,可以确保while (!_shouldStop)在每次循环迭代的顶部都添加我的内容,但是我还有许多业务流程这在它到达下一个循环迭代之前发生,我希望这个取消过程很灵活;不要告诉我我需要在整个工作函数中每4-5行向下循环播放while循环吗?!

我真的希望有更好的方法,如果实际上这是正确的(唯一的)方法,或者他们过去为实现我所追求的目标而使用的方法,是否有人可以向我提供建议。

谢谢帮派。

进一步阅读:所有这些SO响应都假定工作线程将循环。那对我来说并不舒服。如果是线性但及时的后台操作怎么办?


阅读 275

收藏
2020-05-19

共1个答案

一尘不染

不幸的是,可能没有更好的选择。这实际上取决于您的特定情况。这个想法是在安全点优雅地停止线程。这就是为什么Thread.Abort不好的原因所在。因为它不能保证在安全点发生。通过在代码上添加停止机制,可以有效地手动定义安全点。这称为合作取消。基本上有4种广泛的机制可以做到这一点。您可以选择最适合您的情况。

轮询停止标志

您已经提到了此方法。这是很普通的一个。定期检查算法中安全点处的标志,并在收到信号时纾困。标准方法是标记变量volatile。如果这不可能或不方便,则可以使用lock。请记住,您不能标记局部变量,volatile因此,例如,如果lambda表达式是通过闭包捕获的,那么您将不得不采用另一种方法来创建所需的内存屏障。此方法不需要说很多其他内容。

在TPL中使用新的取消机制

这类似于轮询停止标志,除了它使用TPL中的新取消数据结构。它仍然基于协作取消模式。您需要获取CancellationToken和定期检查IsCancellationRequested。要请求取消,你会打电话CancelCancellationTokenSource最初提供的令牌。您可以使用新的取消机制做很多事情。您可以在此处了解更多信息。

使用等待句柄

如果您的工作线程需要在特定间隔内等待或在其正常操作期间等待信号,则此方法很有用。你可以Set一个ManualResetEvent,例如,让线程知道是时候停下来。您可以使用该WaitOne函数测试事件,该函数返回一个bool指示事件是否已发出信号的指示。该WaitOne有一个参数,指定了多少时间等待调用返回如果事件没有在该时间量信号。您可以使用此技术代替Thread.Sleep并同时获得停止指示。如果WaitHandle线程可能还要等待其他实例,这也很有用。您可以一次呼叫WaitHandle.WaitAny以等待任何事件(包括停止事件)。使用事件比打电话更好Thread.Interrupt因为您可以更好地控制程序的流程(Thread.Interrupt抛出异常,所以您必须策略性地放置try- catch块以执行任何必要的清除)。

特殊场景

有几种一次性方案具有非常专门的停止机制。枚举所有这些绝对超出了此答案的范围(没关系,这几乎是不可能的)。我的意思很好的例子是Socket课堂。如果线程被阻塞在通话过程中SendReceive则调用Close将中断对任何阻塞调用它是在有效地疏通它的插座。我确信BCL中还有其他几个类似的技术可以用来解除线程阻塞的区域。

通过中断线程Thread.Interrupt

这样做的好处是它很简单,您不必专注于向代码中撒一些东西。缺点是您几乎无法控制算法中安全点的位置。原因是因为Thread.Interrupt通过在一个固定的BCL阻止调用中注入异常来起作用。这些措施包括Thread.SleepWaitHandle.WaitOneThread.Join等,所以你必须要聪明在哪里放置它们。但是,在大多数情况下,算法会指示它们的去向,而通常情况下这还是很好的,尤其是如果您的算法将大部分时间都用在了这些阻塞调用之一中时。如果您的算法未在BCL中使用阻塞调用之一,则此方法将对您不起作用。这里的理论ThreadInterruptException是只能从.NET等待调用生成,因此很
可能
在安全点。至少您知道该线程不能处于非托管代码中或无法脱离关键部分,而使悬挂的锁处于获取状态。尽管这种方法具有较小的侵入性,但Thread.Abort我仍然不鼓励使用它,因为尚不清楚哪个调用能够响应它,并且许多开发人员将不熟悉它的细微差别。

2020-05-19