9

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

问题

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

下面是方法。

bool方法_

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

ManualResetEvent方法_

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

CancellationToken方法_

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

更新 1:

我发现了一个类似的帖子,得出的结论是该MRE方法比该CancellationToken方法慢得多。有关完整信息,请参阅此处:停止线程、ManualResetEvent、volatile boolean 或 cancelToken

更新 2:

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

更新 3:

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

4

1 回答 1

2

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

重置事件是可以的,你当然可以使用它,但是 CancelellationToken 工作得很好,并且正是为此而设计的。

于 2014-12-10T01:32:40.633 回答