【问题标题】:Techniques for exiting / cancelling while loops across threads: bool, ManualResetEvent or CancellationToken跨线程退出/取消 while 循环的技术:bool、ManualResetEvent 或 CancellationToken
【发布时间】:2015-02-08 00:49:25
【问题描述】:

我正在编写一个有几个线程的程序,每个线程都有一个 while 循环,该循环一直运行直到用户指定它应该停止。我想了几种退出循环和随后线程的方法,并在下面概述了这些方法。

问题

  1. 各有优缺点吗?
  2. 在某些情况下您会使用一种而不使用另一种吗?
  3. 我听说有些人说他们更喜欢 CancellationTokens 来退出线程。与其他两种方法相比,这种方法有什么吸引力?

下面是方法。

bool 方法

最初,我声明了一个 bool,并声明循环一直运行,直到用户将 bool 设置为 false:while(running) { Thread.Sleep(10); /*do work*/ }。然后我思考这是否完全是线程安全的。如果编译器做了一些优化并将 bool 移动到寄存器会怎样。在这种情况下,线程将看到不同的布尔值。因此,我用volatile 关键字标记了布尔值以避免编译器优化。

ManualResetEvent 方法

我的下一个方法是创建一个ManualResetEvent,并说当 WaitOne() 为假时布尔运行:while(!mre.WaitOne(10)) {/*do work*/}。这将阻塞 10 毫秒,然后循环运行,一遍又一遍,直到我们执行 mre.Set() 并且循环退出。

CancellationToken 方法

这种方法我还没有真正尝试过,但我读过几个人们更喜欢用这种方式取消线程的地方。它显然是线程安全的。可以定义一个CancellationTokenSource,将其命名为cts,然后将cts.Token 传递给新线程运行的方法,并使用if 语句检查是否已请求取消:while(!token.IsCancellationRequested) { Thread.Sleep(10); /*do work*/ }

更新 1:

我发现一个类似的帖子得出结论,MRE 方法比CancellationToken 方法慢得多。有关完整信息,请参阅此处:Stopping a Thread, ManualResetEvent, volatile boolean or cancellationToken

更新 2:

在将bool 方法与其他两种方法进行比较时,Eric Lippert 在这里给出了一个很好的答案:AutoResetEvent vs. boolean to stop a thread

更新 3:

我发现了另一条相关信息。 CancellationTokens 一旦被取消就不能被重置。因此,当您只想暂时取消循环以稍后重新启动时,它并不理想。为此,MRE 可能会更好(您可以随心所欲地设置和重置)。

【问题讨论】:

  • 通过 CTS 的协同取消主要在您调用其他也支持取消的方法时非常有用,因为它允许您将令牌向下传播到调用树中。
  • MRE 对象也可以通过调用树向下传递。并且 bool 可以是公共属性,在这种情况下根本不需要传递。
  • 是的,但是由于协作取消模式是 MS 在 .NET 中推动的标准,因此 BCL 类上有几种方法支持 CancellationToken 作为参数。一个示例是 Task.Delay(因此您可以在延迟完成之前取消)。
  • 没错,将 CancellationToken 传递给预定义的 .NET 任务方法允许任务在运行之前检查它是否已经被取消,并且是 CancellationToken 更有益的一个例子。 Task.Run() 是另一个例子......不幸的是我的线程运行时间很长,所以最好不要使用 TPL。

标签: c# multithreading cancellation manualresetevent cancellation-token


【解决方案1】:

大多数情况下,您的线程不会在占用所有 CPU 周期的紧密循环中运行,通常您正在等待某种事件,当您等待时,您无法真正等待布尔值。您的事件可能会超时,等待超时,检查布尔值,然后返回等待。这会产生令人讨厌的代码,也意味着您的线程在超时发生之前不会退出,或者您一直在吃 CPU 检查布尔值。

Reset Event 没问题,你当然可以使用它,但是 CancelellationToken 效果很好,并且专门为此而设计。

【讨论】:

  • 我正在编写的应用程序要求我检查我的应用程序之外的一些条件,因此我没有可以添加任何事件或任何内容来确保它通知我更改。我必须进行某种投票。除了使用 while 循环之外,还可以使用 Timer,它可以定期从线程池中进行轮询(我在适当的情况下使用了这种方法)。不过,这有点超出了本文的范围,更多的是比较取消技术。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多