【发布时间】:2015-02-08 00:49:25
【问题描述】:
我正在编写一个有几个线程的程序,每个线程都有一个 while 循环,该循环一直运行直到用户指定它应该停止。我想了几种退出循环和随后线程的方法,并在下面概述了这些方法。
问题
- 各有优缺点吗?
- 在某些情况下您会使用一种而不使用另一种吗?
- 我听说有些人说他们更喜欢 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