c# - 关于在 .NET 中干净地终止线程的问题

标签 c# .net winforms multithreading

我从我读过的关于该主题的大量文章中了解到 Thread.Abort() 是邪恶的,所以我目前正在删除我的 abort's 以便以更清洁的方式替换它;在比较了 stackoverflow 上人们的用户策略之后,然后阅读了 "How to: Create and Terminate Threads (C# Programming Guide) "来自 MSDN,两者都说明了一种非常相同的方法——即使用 volatile bool方法检查策略,这很好,但我还有一些问题......

对我来说,最突出的一点是,如果您没有一个简单的工作进程,它只是运行一个循环处理代码怎么办?例如对我来说,我的进程是一个后台文件上传进程,我实际上循环遍历每个文件,所以这就是我可以添加我的 while (!_shouldStop)在覆盖我每次循环迭代的顶部,但我有更多的业务流程发生在它到达下一次循环迭代之前,我希望这个取消过程快速;不要告诉我我需要在整个工作函数中每 4-5 行向下撒一次这些 while 循环?!

我真的希望有更好的方法,有人可以告诉我这是否确实是正确的[且唯一的?] 方法,或者他们过去使用的策略来实现我所追求的目标。

谢谢帮。

进一步阅读:All these SO responses假设工作线程将循环。这对我来说并不舒服。如果是线性但及时的后台操作呢?

最佳答案

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

轮询停止标志

你已经提到了这个方法。这是一个很常见的。在算法中的安全点定期检查标志,并在收到信号时退出。标准方法是标记变量 volatile .如果这是不可能的或不方便的,那么您可以使用 lock .请记住,您不能将局部变量标记为 volatile因此,例如,如果 lambda 表达式通过闭包捕获它,那么您将不得不采用不同的方法来创建所需的内存屏障。对于这种方法,无需多说。

使用 TPL 中的新取消机制

这类似于轮询停止标志,不同之处在于它使用 TPL 中的新取消数据结构。它仍然基于合作取消模式。你需要得到一个 CancellationToken和定期检查IsCancellationRequested .要请求取消,您可以调用 CancelCancellationTokenSource最初提供 token 的那个。您可以使用新的取消机制做很多事情。您可以阅读更多关于 here .

使用等待句柄

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

专业场景

有几种一次性方案具有非常专业的停止机制。将它们全部列举出来绝对超出了这个答案的范围(更不用说这几乎是不可能的)。我在这里的意思的一个很好的例子是 Socket类(class)。如果线程在调用 Send 时被阻塞或 Receive然后拨打 Close将在有效解除阻塞的任何阻塞调用上中断套接字。我确信 BCL 中还有其他几个区域可以使用类似的技术来解除线程阻塞。

通过 Thread.Interrupt 中断线程

这里的优点是它很简单,您不必真正专注于在代码中添加任何东西。缺点是您几乎无法控制算法中安全点的位置。原因是因为Thread.Interrupt通过在 jar 装 BCL 阻塞调用之一中注入(inject)异常来工作。其中包括 Thread.Sleep , WaitHandle.WaitOne , Thread.Join等。所以你必须明智地放置它们。然而,大多数时候算法决定了它们去哪里,这通常很好,特别是如果你的算法大部分时间都花在这些阻塞调用之一上。如果您的算法不使用 BCL 中的阻塞调用之一,则此方法对您不起作用。这里的理论是ThreadInterruptException仅从 .NET 等待调用生成,因此它可能处于安全点。至少您知道该线程不能处于非托管代码中,也不能退出临界区而使悬空锁处于已获取状态。尽管这比 Thread.Abort 侵入性小我仍然不鼓励使用它,因为哪个调用响应它并不明显,而且许多开发人员将不熟悉它的细微差别。

关于c# - 关于在 .NET 中干净地终止线程的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3632149/

相关文章:

c# - ClientWebsocket SSL 证书验证失败

c# - C# .net 中的这个声明是什么意思?

C# 面板对接问题

c# - NumericUpDown 控件可以做到这一点吗?

asp.net - Web 应用程序与遗留代码的互操作

c# - 为什么不能 <input type ="hidden"> 从服务器端接受 bool 值?

C# 是否可以自定义/覆盖保留其类名的类?

c# - 有没有一种方法可以使用 Kinect SDK 检测我的手是张开还是闭合?

c# - 为什么 double.IsNaN 有如此奇怪的实现

c# - 从两个列表中计算所有可能的项目对?