监视器、锁定或易失性
本文关键字:易失性 锁定 监视器 | 更新日期: 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))
{
..........
}
});