System.Timers.Timer间隔重置导致重新启动

4

我在一个Windows服务中遇到了System.Timers.Timer的问题。我基本上是使用以下代码在一个基础轮询服务类中设置计时器:

_serviceTimer.Elapsed += OnElapsedTime;
_serviceTimer.Interval = ServiceTimerInterval.TotalMilliseconds;
_serviceTimer.AutoReset = false;
_serviceTimer.Enabled = true;

当OnElapsedTime触发时,我想禁用定时器并根据查找结果设置不同的间隔时间。问题在于,当我更改间隔时间时,它实际上会重新启动定时器。这种奇怪的行为在msndn文档中有提到:
注意: 如果Enabled和AutoReset都设置为false,并且之前已启用定时器,则设置Interval属性会导致Elapsed事件被触发一次,就像Enabled属性被设置为true一样。要设置间隔而不触发事件,可以暂时将AutoReset属性设置为true。 Timer.Interval 在onelapsed事件中,我有以下代码:
_serviceTimer.Enabled = false;
double newIntervalSetting = newSetting;
base._serviceTimer.AutoReset = true;
base._serviceTimer.Interval = newIntervalSetting;
base._serviceTimer.AutoReset = false;
//reenable after processing

问题在于,尽管我在更改间隔之前将自动重置设置为true,但时间间隔的更改仍会开始计时器倒计时并最终触发事件。启用始终保持不变,但事件仍然触发。我不确定是否误解了有关正确操作方式的MSDN文档。有人能帮我吗?

是的,这确实是一个糟糕的类。这就是为什么有System.Threading.Timer存在的原因。 - Hans Passant
是的,它肯定有一些限制。我想我会使用下面的快捷方式,然后在有机会时切换它。 - gleasonomicon
2个回答

2
我认为这与事件处理程序被从不同的线程调用有关,而当前设置Enabledfalse的代码也在另一个线程中执行。
根据MSDN文档
“引发Elapsed事件的信号总是排队等待在ThreadPool线程上执行,因此事件处理方法可能会在一个线程上运行,同时Stop方法在另一个线程上运行。这可能导致Elapsed事件在调用Stop方法后才被触发。下一节中的代码示例展示了解决这个竞争条件的一种方式。”
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; }
    }
}

1

你能在现有的OnElapsedTime事件中设置一个布尔值m_SetEnabled = true,然后添加if(m_SetEnabled) { m_SetEnabled = false; return; }来忽略只触发一次的事件吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接