我不明白为什么在这个实现中stopped
不是volatile
- 如果另一个线程更新,它会被正确反映出来吗?
其次是测试(!Stopping
)原子?
using System;
using System.Threading;
/// <summary>
/// Skeleton for a worker thread. Another thread would typically set up
/// an instance with some work to do, and invoke the Run method (eg with
/// new Thread(new ThreadStart(job.Run)).Start())
/// </summary>
public class Worker
{
/// <summary>
/// Lock covering stopping and stopped
/// </summary>
readonly object stopLock = new object();
/// <summary>
/// Whether or not the worker thread has been asked to stop
/// </summary>
bool stopping = false;
/// <summary>
/// Whether or not the worker thread has stopped
/// </summary>
bool stopped = false;
/// <summary>
/// Returns whether the worker thread has been asked to stop.
/// This continues to return true even after the thread has stopped.
/// </summary>
public bool Stopping
{
get
{
lock (stopLock)
{
return stopping;
}
}
}
/// <summary>
/// Returns whether the worker thread has stopped.
/// </summary>
public bool Stopped
{
get
{
lock (stopLock)
{
return stopped;
}
}
}
/// <summary>
/// Tells the worker thread to stop, typically after completing its
/// current work item. (The thread is *not* guaranteed to have stopped
/// by the time this method returns.)
/// </summary>
public void Stop()
{
lock (stopLock)
{
stopping = true;
}
}
/// <summary>
/// Called by the worker thread to indicate when it has stopped.
/// </summary>
void SetStopped()
{
lock (stopLock)
{
stopped = true;
}
}
/// <summary>
/// Main work loop of the class.
/// </summary>
public void Run()
{
try
{
while (!Stopping)
{
// Insert work here. Make sure it doesn't tight loop!
// (If work is arriving periodically, use a queue and Monitor.Wait,
// changing the Stop method to pulse the monitor as well as setting
// stopping.)
// Note that you may also wish to break out *within* the loop
// if work items can take a very long time but have points at which
// it makes sense to check whether or not you've been asked to stop.
// Do this with just:
// if (Stopping)
// {
// return;
// }
// The finally block will make sure that the stopped flag is set.
}
}
finally
{
SetStopped();
}
}
}
修改
答案 0 :(得分:6)
因为只能在lock
内访问它。 lock
也可以确保您看到最新的值。
重新原子性(我假设你真的意味着在这里同步?);它没有任何可能性;即使Stopping
已同步,我们也无法再在退出lock
后立即相信该值是最新的。因此,!Stopping
与Stopping
的同步性不会更高或更低。重要的是我们知道我们最近至少检查过了。有一个边缘情况,我们检查之后更改了标志,但这很好:当我们检查时,我们应该继续这样做。
答案 1 :(得分:2)
此代码的行为在C#语言规范的第3.10节中定义:
执行C#程序,以便在关键执行点保留每个执行线程的副作用。副作用定义为易失性字段的读取或写入,对非易失性变量的写入,对外部资源的写入以及抛出异常。必须保留这些副作用的顺序的关键执行点是对volatile字段(第10.5.3节),锁定语句(第8.12节)以及线程创建和终止的引用。
换句话说,lock语句足以保证避免声明停止的字段volatile。
如果lock语句仅调用Monitor.Enter()和Exit()方法,那么JIT编译器如何实现此规则是一个有趣的问题。我不认为它对这些方法有特殊的了解,我认为这是输入在Enter()调用后启动的try块的副作用。但这只是猜测。
答案 2 :(得分:1)
见this entry on SO。它解释了为什么锁优先于易失性。