在Timer HandleElapsed处理程序中使用Interlocked.CompareExchange同步与同步对象

时间:2013-08-15 09:10:22

标签: c# multithreading timer interlocked

我正在阅读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。根本没有互锁。

1 个答案:

答案 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}某处(如果多次使用同一个线程,至少要将线程返回MemoryBarrierThreadPool