Windows Service中Timer设AutoReset=False仍重复触发问题求助
你遇到的问题核心在于对System.Timers.Timer的工作机制理解有偏差,尤其是AutoReset=false和Start()、Interval修改的行为逻辑,咱们一步步拆解:
为什么事件会重复触发?
你设置了AutoReset=false,这意味着Timer默认只会触发一次,但你在timer1_Tick方法末尾调用了timer1.Start()——这个操作会重新启动计时器,从调用Start()的时刻开始,按照当前设置的Interval等待下一次触发。
问题出在:你的业务代码运行时长超过了设置的60000ms(1分钟),而Start()是在进入else分支后就设置了Interval=60000,然后执行业务代码,最后调用Start()。当业务代码运行超过1分钟时,上一次调用Start()启动的计时器已经走完了60秒的间隔,于是会再次触发Elapsed事件(因为ThreadPool会为Timer回调分配新线程),此时前一个Tick方法还在执行,就出现了重复触发的情况。
你之前尝试的timer1.Enabled = false; timer1.Stop();没解决问题,是因为你可能在修改Interval前调用,但之后又调用了Start(),而且业务代码执行时间过长,还是会导致新的触发。
修改Interval会自动重启Timer吗?
是的,对于System.Timers.Timer,当Timer处于**启用状态(Enabled=true)**时,修改Interval会自动重置计时器——也就是说,计时器会从修改Interval的那一刻开始,重新计算间隔时间,而不是继续之前未走完的时长。
如果Timer是禁用状态,修改Interval不会触发重启,需要手动调用Start()或设置Enabled=true。
正确的实现方式
要避免重复触发,你需要确保同一时间只有一个Tick方法在执行,并且在整个Tick逻辑执行完成后,再设置间隔并启动Timer。这里提供两种可行方案:
方案1:用锁控制并发
在Tick方法中加锁,确保同一时间只有一个线程执行逻辑:
private readonly object _timerLock = new object(); private void timer1_Tick(object sender, ElapsedEventArgs e) { // 先禁用Timer,防止在获取锁前再次触发 timer1.Enabled = false; if (!Monitor.TryEnter(_timerLock)) { // 如果已有线程在执行,直接退出,稍后再尝试 timer1.Enabled = true; return; } try { if (!IsServerConnected(db.Connectionstring)) { //错误处理 timer1.Interval = 300000; } else { if (!Directory.Exists(cfg.CSVPath)) { //错误处理 timer1.Interval = 300000; } else { timer1.Interval = 60000; //业务代码(运行时长超过1分钟) } } } finally { Monitor.Exit(_timerLock); // 执行完所有逻辑后,再启动Timer timer1.Enabled = true; } }
这里把Start()换成了设置Enabled=true,效果一致,而且进入方法时先禁用Timer,避免锁竞争期间触发新事件。
方案2:调整Timer启动时机
既然AutoReset=false,我们可以不在方法末尾调用Start(),而是在所有逻辑执行完成后,根据最终的Interval设置来启动Timer,确保执行逻辑期间Timer是禁用的:
private void timer1_Tick(object sender, ElapsedEventArgs e) { // 立即禁用Timer,防止重复触发 timer1.Enabled = false; try { if (!IsServerConnected(db.Connectionstring)) { //错误处理 timer1.Interval = 300000; } else { if (!Directory.Exists(cfg.CSVPath)) { //错误处理 timer1.Interval = 300000; } else { timer1.Interval = 60000; //业务代码(运行时长超过1分钟) } } } finally { // 所有逻辑执行完毕后,再启动Timer,下一次触发会在Interval之后 timer1.Enabled = true; } }
这种方式下,Timer只会在当前Tick逻辑完全执行完后才启动下一轮计时,彻底避免了业务代码过长导致的重复触发。
另外初始化代码可以简化:
private Timer timer1 = new Timer(6000); // 直接在构造函数设置间隔 timer1.Elapsed += timer1_Tick; timer1.AutoReset = false; timer1.Enabled = true;
内容的提问来源于stack exchange,提问作者zedgraphsad




