在Silverlight应用程序中,我有一段代码必须每500毫秒运行一次。我计划使用DispatcherTimer来实现这一点(参见下面的代码)。
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 500); // 500 Milliseconds
dt.Tick += new EventHandler(dt_Tick);
dt.Start();
但是,可能会发生代码块执行时间超过500毫秒(代码块执行一些webservice调用)。如何确保当前正在进行呼叫,DispatcherTimer不会触发另一个事件?有什么选择,最好的方法是什么?使用锁?
答案 0 :(得分:5)
仅DispatcherTimer
在调度程序线程上运行 - 因此您无法同时运行两个处理程序。他们可能会排队等候并直接运行一个,当然 - 你应该检查一下。
但是,不应在DispatcherTimer
中进行网络服务调用。在后台线程中执行此操作,否则您将阻止UI在您等待Web服务的所有时间进行更新。基本上你不应该在UI线程中做任何长时间运行的工作。使用其他各种计时器之一(例如System.Timers.Timer
)定期在线程池线程上执行工作,并在需要在UI上显示某些数据时使用调度程序回调UI线程
当然,现在你已经遇到了多种线程同时多次同时触发的新型定时器的潜在问题。避免这种情况的一个选择是将AutoReset
属性设置为false,并且只安排当前结束时的下一个计时器滴答。
答案 1 :(得分:1)
我会说你跳过一个勾号,如果花了太长时间,否则你会因为锁而得到一个巨大的队列。
所以在事件处理程序中说:
if(!busy) {
busy = true;
// some code which could take longer than 500 ms
busy = false;
}
答案 2 :(得分:1)
我不知道DispatchTimer是否有任何聪明的方法可以做到这一点,但在这种情况下我会做的不是试图让计时器不触发事件,而是让事件在没有的情况下什么都不做完成了之前的运行。
您可以通过在事件处理程序的开头获取锁来使用锁来执行此操作。如果锁定不可用,则退出该功能(已经运行),如果锁定完成工作,然后一旦完成工作就释放锁定。
您想要的方法是Monitor.TryEnter,并且您需要确保正确执行错误捕获,就像使用任何锁一样。
答案 3 :(得分:1)
为了使事件成功运行而不再从DispatcherTimer调用,在前一个tick中完成stop
调度程序计时器,在输入dt_Tick
事件后和tick事件结束时再次调用start
,这将再次将DispatcherTimer的IsEnabled初始化为true。