如何保证System.Timers.Timer触发的Elapsed事件处理程序按顺序执行
解决方案
System.Timers.Timer 默认会在线程池线程上触发 Elapsed 事件,如果前一次事件处理未完成,到达间隔时间后会再次拉起新的线程执行处理逻辑,因此会出现并发执行、顺序混乱的问题,以下是两种常用的实现顺序执行的方案:
方案1:通过 AutoReset 属性控制单次触发
实现思路
将 Timer 的 AutoReset 属性设为 false,让 Timer 仅触发一次 Elapsed 事件,待本次事件处理逻辑完全执行完成后,再手动重启 Timer,从根源上避免并发触发。
示例代码
// 声明静态Timer方便事件处理中访问 private static System.Timers.Timer _timer; static void Main(string[] args) { _timer = new System.Timers.Timer(1000); // 设为false表示仅触发一次Elapsed事件 _timer.AutoReset = false; _timer.Elapsed += (sender, e) => { try { PrintToConsole(); } finally { // 处理完成后重启Timer,保证下一次触发在上次处理结束后才开始计时 _timer.Start(); } }; _timer.Start(); Console.ReadLine(); } private static void PrintToConsole() { var randomInt = new Random().Next(1000); Console.WriteLine(randomInt); Thread.Sleep(2000); Console.WriteLine(randomInt); }
特点
两次事件触发的间隔为「事件处理耗时 + 设定的1秒间隔」,不会出现事件堆积的问题,适合对两次触发的间隔精度要求不高、不允许事件并发的场景。
方案2:通过锁实现排队执行
实现思路
通过锁对象同步事件处理逻辑,即使 Elapsed 事件多次并发触发,也必须等待前一次处理逻辑释放锁后才能执行,保证执行顺序和触发顺序一致。
示例代码
// 全局锁对象 private static readonly object _syncLock = new object(); static void Main(string[] args) { var timer = new System.Timers.Timer(1000); timer.Elapsed += (sender, e) => PrintToConsole(); timer.Start(); Console.ReadLine(); } private static void PrintToConsole() { // 进入逻辑前先获取锁,未获取到则阻塞等待前一次处理完成 lock (_syncLock) { var randomInt = new Random().Next(1000); Console.WriteLine(randomInt); Thread.Sleep(2000); Console.WriteLine(randomInt); } }
特点
Timer 仍然按照固定1秒间隔触发事件,但是事件处理逻辑会排队执行,如果事件处理耗时长期大于触发间隔,会导致线程池线程堆积,适合处理耗时稳定、不会长期超过触发间隔的场景。
内容的提问来源于stack exchange,提问作者Jimmy
相关产品推荐
相关产品推荐

