System.Threading.Timer运行异常问题排查求助
问题分析与解决方案
咱们直接说核心问题:你的定时器出现输出丢失,根源在于回调函数的长时间阻塞和错误的时间控制逻辑,具体拆解来看:
1. System.Threading.Timer的工作机制导致触发丢失
System.Threading.Timer是靠线程池线程执行回调的,它有个关键规则:如果上一次回调还在运行,当下一次触发时间到了的时候,系统会直接跳过这次触发(不会排队等待)。
你的代码里,TimerCallback里用Thread.Sleep(10000)模拟了10秒的长耗时操作,但定时器的间隔却设成了1000ms(1秒)。这意味着每次回调会霸占线程池线程10秒,在这10秒里,定时器原本该触发的10次回调全被跳过了。等回调结束后,定时器才会继续按间隔触发,但此时你的countDown已经少减了好多次,自然会出现时间跳变、漏输出的情况。
2. 基于计数的时间控制逻辑完全不可靠
你用countDown从60递减到0来触发每分钟输出,这个逻辑完全依赖回调被精准执行60次(每次间隔1秒)。可一旦回调被阻塞或跳过,countDown的递减节奏就乱了——要么实际过了1分钟但countDown还没到0,要么countDown到0时实际时间已经超了1分钟,甚至直接跳过该输出的时间点。
修正后的代码方案
我给你两种修复思路,按需选择:
方案一:直接用每分钟间隔的定时器,耗时操作异步处理
适合不需要每秒执行额外逻辑,只需要每分钟输出一次的场景:
class Program { static Timer m_Timer; static void Main(string[] args) { // 配置定时器:立即执行第一次,之后每隔60秒执行一次 m_Timer = new Timer(TimerCallback, null, 0, 60000); Console.WriteLine("定时器已启动,按任意键退出..."); Console.ReadKey(); // 记得释放定时器资源 m_Timer.Dispose(); } static void TimerCallback(Object o) { Console.WriteLine(" ===>>>>>>" + DateTime.Now.ToString()); // 把耗时操作丢到异步任务里,绝不阻塞回调线程 Task.Run(() => { // 模拟长时间运行的代码 Thread.Sleep(10000); Console.WriteLine("耗时操作完成:" + DateTime.Now.ToString()); }); } }
方案二:保留1秒间隔,基于时间差判断输出
适合需要每秒执行其他逻辑,同时每分钟输出一次的场景:
class Program { static Timer m_Timer; static DateTime _lastPrintTime; static void Main(string[] args) { _lastPrintTime = DateTime.Now; m_Timer = new Timer(TimerCallback, null, 0, 1000); Console.WriteLine("定时器已启动,按任意键退出..."); Console.ReadKey(); m_Timer.Dispose(); } static void TimerCallback(Object o) { var now = DateTime.Now; // 检查距离上次打印是否过了1分钟(允许少量误差) if (now.Subtract(_lastPrintTime).TotalMinutes >= 1) { Console.WriteLine(" ===>>>>>>" + now.ToString()); _lastPrintTime = now; } // 耗时操作异步处理,不影响定时器触发 Task.Run(() => { Thread.Sleep(10000); }); } }
额外提醒
- 绝对不要在
System.Threading.Timer的回调里做长时间同步操作,线程池线程是共享资源,阻塞会拖垮其他依赖线程池的功能。 - 如果你的耗时操作必须保证每次触发都被处理(不能跳过),可以考虑用
System.Timers.Timer并设置AutoReset = false,在回调结束后手动重置定时器,同时也要异步处理耗时逻辑。
内容的提问来源于stack exchange,提问作者Leh
相关产品推荐
相关产品推荐

