5

我有一个定期处理队列的 Windows 服务(.NET 4),例如每 15 分钟一次。我使用System.Threading.Timer在服务开始每 X 毫秒触发一次回调时设置的。通常每次运行都需要几秒钟并且永远不会发生碰撞,但是如果我不能假设 - 那么我希望下一次运行在处理正在进行时立即退出。

这可以通过 lock、volatile bool 或监视器轻松解决,但是在这种情况下实际上适合使用什么,或者只是一般的首选选项?

我发现其他帖子几乎可以回答这种情况(例如Volatile vs. Interlocked vs. lock),但需要一些建议将其扩展到立即退出的 Timer 示例。

4

3 回答 3

6

您不需要任何锁,您应该从计时器委托中重新安排下一个计时器执行。这应该确保 100% 没有重叠。

在计时器的事件处理程序调用结束时,计时器timer.Change(nextRunInMilliseconds, Timeout.Infinite)只会在 之后触发一次nextRunInMilliseconds

例子:

//Object that holds timer state, and possible additional data
private class TimerState
{
    public Timer Timer { get; set; }
    public bool Stop { get; set; }
}

public void Run()
{
    var timerState = new TimerState();
    //Create the timer but don't start it
    timerState.Timer = new Timer(OnTimer, timerState, Timeout.Infinite, Timeout.Infinite);
    //Start the timer
    timerState.Timer.Change(1000, Timeout.Infinite);
}

public void OnTimer(object state)
{
    var timerState = (TimerState) state;            
    try
    {
        //Do work
    }
    finally 
    {
        //Reschedule timer
        if (!timerState.Stop)
            timerState.Timer.Change(1000, Timeout.Infinite);
    }
}
于 2013-06-24T09:10:18.603 回答
1

好吧,他们中的任何一个都会做这项工作。Monitorvia通常容易使用lock,但在这种情况下你不能使用lock,因为你需要指定一个零超时;因此,最简单的方法可能是CompareExchange

private int isRunning;
...
if(Interlocked.CompareExchange(ref isRunning, 1, 0) == 0) {
    try {
        // your work
    } finally {
        Interlocked.Exchange(ref isRunning, 0);
    }
}

做同样的事情Monitor是:

private readonly object syncLock = new object();
...
bool lockTaken = false;
try {
    Monitor.TryEnter(syncLock, 0, ref lockTaken);
    if (lockTaken) {
        // your work
    }
} finally {
    if(lockTaken) Monitor.Exit(syncLock);
}
于 2013-06-24T09:11:39.013 回答
0

我认为,如果您发现需要同步计时器委托 - 您做错了,并且Timer可能不是您想要使用的类。恕我直言,最好:

1)要么保持Timer,但将间隔值增加到可以安全假设的点,即线程不会出现问题,

2)或删除Timer并改用简单的线程。你知道的,比如:

var t = new Thread();
t.Start(() =>
         {
            while (!_stopEvent.WaitOne(100))
            {
                 ..........
            }
         });
于 2013-06-24T09:32:33.057 回答