C# 优雅地关闭工作线程
我不明白为什么在这个实现中,C# 优雅地关闭工作线程,c#,.net,concurrency,multithreading,C#,.net,Concurrency,Multithreading,我不明白为什么在这个实现中,stopped不是volatile——如果一个不同的线程更新了它,它会正确地反映出来吗 第二,测试(!Stopping)是原子的吗 使用系统; 使用系统线程; /// ///工作线程的骨架。通常会设置另一个线程 ///一个有一些工作要做的实例,并调用Run方法(例如 ///新线程(新线程开始(job.Run)).Start()) /// 公社工人 { /// ///锁盖停止和停止 /// 只读对象stopLock=新对象(); /// ///是否已要求工作线
stopped
不是volatile
——如果一个不同的线程更新了它,它会正确地反映出来吗
第二,测试(!Stopping
)是原子的吗
使用系统;
使用系统线程;
///
///工作线程的骨架。通常会设置另一个线程
///一个有一些工作要做的实例,并调用Run方法(例如
///新线程(新线程开始(job.Run)).Start())
///
公社工人
{
///
///锁盖停止和停止
///
只读对象stopLock=新对象();
///
///是否已要求工作线程停止
///
布尔停止=假;
///
///工作线程是否已停止
///
bool-stopped=false;
///
///返回是否已要求工作线程停止。
///即使在线程停止后,它仍会继续返回true。
///
公共停车场
{
得到
{
锁(止动锁)
{
回程停车;
}
}
}
///
///返回工作线程是否已停止。
///
公共广播停止了
{
得到
{
锁(止动锁)
{
返回停止;
}
}
}
///
///通知工作线程停止,通常在完成其
///当前工作项。(线程*不*保证已停止。)
///当此方法返回时。)
///
公共停车场()
{
锁(止动锁)
{
停止=真;
}
}
///
///由工作线程调用以指示其何时停止。
///
无效
{
锁(止动锁)
{
停止=真;
}
}
///
///本课程的主要工作循环。
///
公开募捐
{
尝试
{
当(!停止)
{
//在这里插入工作。确保它不会出现紧密循环!
//(如果工作定期到达,使用队列和监视器。等待,
//更改停止方法以使监视器脉冲以及设置
//停下来。)
//请注意,您可能还希望在循环中突破
//如果工作项可能需要很长的时间,但有
//检查你是否被要求停止是有意义的。
//只需执行以下操作:
//如果(停止)
// {
//返回;
// }
//finally块将确保设置了stopped标志。
}
}
最后
{
SetStopped();
}
}
}
编辑
此代码来自 因为它只能在
锁内访问。lock
还可以确保您看到最新的值
重原子性(我想你真的是指这里的同步?);这没有什么困难;即使停止
是同步的,我们也不能再相信一旦我们退出我们拥有的锁
,该值就是最新的。因此!停止
与停止
的同步性不一样。重要的是我们知道我们至少最近检查过了。有一种边缘情况,即标志在我们检查之后被更改,但这很好:当我们检查时,我们确实应该继续。请参阅。它解释了为什么锁优先于volatile。此代码的行为在C语言规范的第3.10节中定义:
C#程序的执行过程是这样进行的,即每个执行线程的副作用都保留在关键执行点。副作用定义为对易失性字段的读取或写入、对非易失性变量的写入、对外部资源的写入以及引发异常。必须保留这些副作用顺序的关键执行点是对易失性字段(§10.5.3)、锁定语句(§8.12)以及线程创建和终止的引用
换句话说,lock语句足以保证避免声明stopped字段为volatile
JIT编译器如何实现此规则是一个有趣的问题,因为lock语句只调用Monitor.Enter()和Exit()方法。我不认为它对这些方法有什么特别的了解,我认为这是输入在Enter()调用之后开始的try块的副作用。但这只是一个猜测。我不明白,Jon Skeet是如何关联的?@s.Mark:Its'来自Jon Skeets页面关于关闭工作线程的内容。@s.Mark…任何与编程相关的内容都与Jon Skeet有关……这应该是Jon Skeet的事实!但是这个例子不属于链接条目中的“volatile实际上有什么好处”类别吗?没有方法将stopped或stopping设置为false,因此不可能存在任何竞争条件。我理解这一点了吗?每次获得一个锁时,寄存器都会被刷新,并且您从不读取过时的值?换言之,如果我在一个紧循环中获得一个锁,那么我不需要volatile?曾经吗?只要所有其他线程仅在持有(相同)锁时更新字段,则是。如果其他线程在未获得锁的情况下更新字段,则所有下注都将取消。
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();
}
}
}