1

我正在阅读 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 块末尾的变量同步点的分配。它根本没有联锁。

4

1 回答 1

1

这是原子的,所以线程安全

int sync = Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)

这意味着:如果同步点为 0,则将其设置为 1,给我旧值(因此为 0)并将其同步(局部变量)。然后检查交换是否完成(如果旧值为 0)

我会有所不同的是

syncPoint = 0;

我会用

Interlocked.Exchange(ref syncPoint, 0);

只是为了确保其他线程可以立即看到“锁”是空闲的,否则当前线程可能会延迟“写入”,syncPoint = 0并且没有其他线程可以进入“锁”。

在给出的示例中,这不是问题:HandleElapsed被调用以响应事件(计时器可能使用操作系统计时器)......我什至无法想象在事件处理后没有任何代码生成内存屏障(在 .NET 代码内部或 Windows 操作系统代码内部),以便其他线程可以看到更新syncPoint。唯一的区别是它可能会在“该点下方数百行”而不是“立即”显示。

让我们检查一下这个理论:从这里

如果 SynchronizingObject 属性为 null,则在 ThreadPool 线程上引发 Elapsed 事件。如果 Elapsed 事件的处理持续时间超过 Interval,则该事件可能会在另一个 ThreadPool 线程上再次引发。在这种情况下,事件处理程序应该是可重入的。

所以,事件是在一个新ThreadPool线程上触发的......在事件之后肯定会返回ThreadPool......好吧......肯定有一个MemoryBarrier地方(至少将线程返回到ThreadPool或者Sleep如果同一个线程是多次使用)

于 2013-08-15T09:25:31.680 回答