我正在阅读MSDN示例http://msdn.microsoft.com/en-us/library/system.timers.timer.stop.aspx
在timer.stop示例中,我怀疑它使用Interlocked.CompareExchange的方式不对。
private static void HandleElapsed(object sender, ElapsedEventArgs e)
{
numEvents += 1;
// This example assumes that overlapping events can be
// discarded. That is, if an Elapsed event is raised before
// the previous event is finished processing, the second
// event is ignored.
//
// CompareExchange is used to take control of syncPoint,
// and to determine whether the attempt was successful.
// CompareExchange attempts to put 1 into syncPoint, but
// only if the current value of syncPoint is zero
// (specified by the third parameter). If another thread
// has set syncPoint to 1, or if the control thread has
// set syncPoint to -1, the current event is skipped.
// (Normally it would not be necessary to use a local
// variable for the return value. A local variable is
// used here to determine the reason the event was
// skipped.)
//
int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)
{
// No other event was executing.
// The event handler simulates an amount of work
// lasting between 50 and 200 milliseconds, so that
// some events will overlap.
int delay = timerIntervalBase
- timerIntervalDelta / 2 + rand.Next(timerIntervalDelta);
Thread.Sleep(delay);
numExecuted += 1;
// Release control of syncPoint.
syncPoint = 0;
}
else
{
if (sync == 1) { numSkipped += 1; } else { numLate += 1; }
}
}
我的问题是这个块
int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)
{
// lots of code here
syncPoint = 0;
}
应该是
int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)
{
// lots of code here
Interlocked.CompareExchange(ref syncPoint, 0, 1)
}
因为原始分配syncPoint = 0;
不是线程安全的。我是对的吗?
更新:
我更新了这个例子,我对ComareExchange的返回值没有疑问,我的问题是关于在这个if块的末尾分配变量syncpoint。根本没有互锁。
答案 0 :(得分:1)
这是原子的,所以线程安全
int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)
这意味着:如果syncPoint为0,则将其设置为1,给我旧值(如此0)并将其置于同步(本地变量)。然后检查交换是否完成(如果旧值为0)
我将与众不同的是
syncPoint = 0;
我会用
Interlocked.Exchange(ref syncPoint, 0);
只是为了确保其他线程可以立即看到“锁定”是空闲的,否则当前线程可能会延迟“写入”syncPoint = 0
并且没有其他线程可以进入“锁定”。
BUT
在给出的示例中这不是问题:调用HandleElapsed
以响应事件(Timer可能使用OS计时器)...我甚至无法想象在事件处理之后不是任何生成内存障碍的代码(在.NET代码内部或在Windows操作系统代码内部),因此syncPoint
更新将对其他线程可见。唯一的区别是,它可能会被显示为“在该点以下几百行”,而不是“立即现在”。
让我们检查一下这个理论:来自here
如果SynchronizingObject属性为null,则在ThreadPool线程上引发Elapsed事件。如果Elapsed事件的处理持续时间超过Interval,则可能会在另一个ThreadPool线程上再次引发该事件。在这种情况下,事件处理程序应该是可重入的。
所以,事件是在一个新的ThreadPool
线程上触发的...事件发生后肯定会返回到ThreadPool
......好的...肯定有一个{{1}某处(如果多次使用同一个线程,至少要将线程返回MemoryBarrier
或ThreadPool
)