我正在编写一个有几个线程的程序,每个线程都有一个 while 循环,直到用户指定它应该停止。我想到了几种退出循环的方法,然后是线程,并在下面概述了这些方法。
问题
- 每个都有优点和缺点吗?
- 是否存在您会使用一种而不使用另一种的情况?
- 我听说有些人说他们更喜欢 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 可能会更好(您可以随心所欲地设置和重置)。