我需要在一定间隔(例如,循环中的5到5分钟)进行一些操作按钮)。
我正在考虑使用计时器类,但是即使停止了计时器,事件也可能会发射。
我如何在计时器上运行一些代码,并且仍然可以立即将所有内容都带入完整的停止?
只是这样我才适当地理解:通过完全停止,我的意思是事件停止,我可以处理像计时器本身等的对象。我不是在问如何避免在计时器是在计时器之后发射的意外事件中产生副作用停了!
对此问题的回答很大程度上取决于您的操作类型。
最佳场景是运行一个带有循环的线程并收听流产事件。
static AutoResetEvent abort = new AutoResetEvent();
Thread worker = new Thread(WorkerThread);
void MainThread()
{
worker.Start();
Thread.Sleep(30000);
abort.Set();
}
void WorkerThread()
{
while(true)
{
if(abort.WaitOne(5000)) return;
// DO YOUR JOB
}
}
当您从另一个线程调用abort.Set()
时,该线程将退出。
但是,如果您的代码长期运行,则在工作完成之前您将无法退出。要立即退出您,您将不得不中止线程,但由于资源消耗,这并不明智。
另外,如果您的操作很长(假设您正在经历长数组),则可以不时检查"流产"事件状态(例如,循环的每一个迭代),例如此abort.WaitOne(0)
。
带有计时器的竞赛条件是不可避免的,因为正如您所说,回调是从线程池执行的。但是,我相信即使仍在执行事件,您也可以安全地处理计时器。可能会有所帮助的选项是,如果您考虑使用System.Threading.Timer
而不是System.Timers.Timer
,则可以调用Timer.dispose(Waithandle),如果您需要有办法知道计时器事件何时完成执行。这将防止在您还需要处置其他一些资源的情况下的种族条件 - 事件消费者功能将尝试使用的资源。
至于"立即"要求,最直接的可能是使用同步原始的东西来停止执行。例如,考虑以下方式:
static System.Timers.Timer timer;
static void Main(string[] args)
{
var cancelSource = new CancellationTokenSource();
timer = new System.Timers.Timer(200);
timer.Elapsed += new SomeTimerConsumer(cancelSource.Token).timer_Elapsed;
timer.Start();
// Let it run for a while
Thread.Sleep(5000);
// Stop "immediately"
cancelSource.Cancel(); // Tell running events to finish ASAP
lock (timer)
timer.Dispose();
}
class SomeTimerConsumer
{
private CancellationToken cancelTimer;
public SomeTimerConsumer(CancellationToken cancelTimer)
{
this.cancelTimer = cancelTimer;
}
public void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
lock (timer)
{
// Do some potentially long operation, that respects cancellation requests
if (cancelTimer.IsCancellationRequested)
return;
// More stuff here
}
}
}
这是一个玩具示例,但它说明了我的观点。执行"立即停止"的3行具有以下功能:
- 到
Dispose
呼叫返回时,// More stuff here
代码都不会再次执行。 - 由于锁定计时器时,
// More stuff here
代码都无法执行。 - 前两个功能需要锁定,但是它们阻止了计时器"立即"停止,因为进入锁时,需要等待所有计时器事件呼叫,如果它们启动时完成。因此
注意:如果您需要多个计时器事件才能同时执行,请考虑使用ReaderWriterLockSlim
而不是监视器。
我会考虑以下两个选项之一:
-
对您需要执行的事件进行安全检查。类似于数据库标志。因此,即使计时器未能停止事件,安全检查失败时也会救出。
-
使用Quartz.net之类的内容进行调度。这确实是沉重的手,但它会做您想要的。