监视器、锁定或易失性

本文关键字:易失性 锁定 监视器 | 更新日期: 2023-09-27 18:19:48

我有一个windows服务(.NET 4),它定期处理队列,例如每15分钟处理一次。我使用System.Threading.Timer,它是在服务每X毫秒启动一次回调时设置的。通常情况下,每次运行都需要几秒钟的时间,而且从不发生冲突,但如果我不能假设这一点怎么办?如果处理正在进行,我希望下一次运行立即退出。

使用锁、volatile bool或监视器可以很容易地解决这一问题,但在这种情况下,实际适合使用什么,或者只是一般的首选选项?

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

监视器、锁定或易失性

您不需要任何锁,只需从计时器委托中重新安排下一次计时器执行即可。这应该确保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);
    }
}

好吧,他们中的任何一个都能胜任这项工作。Monitor通常通过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);
}

我认为,如果您发现需要同步定时器委托,那么您做得不对,Timer可能不是您想要使用的类。Imho更好:

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

2) 或者删除CCD_ 11并使用简单的Thread。比如:

var t = new Thread();
t.Start(() =>
         {
            while (!_stopEvent.WaitOne(100))
            {
                 ..........
            }
         });