C# Windows服务定时器失效:启动即重复执行、后续不触发任务
问题根因
- 所谓“多线程重复执行”是日志写入逻辑的假象
你定义的类级StringBuilder实例从未做过清空操作,所有追加的日志内容会一直累积在内存中;且每次写日志时,都会把当前StringBuilder中存储的全部历史内容追加到文件,而非只写本次新生成的日志行。
按你当前代码的执行顺序,实际写日志的流程完全对应你看到的输出:- OnStart开头往sb加第一行「Service started up」,把sb全部1行内容写入日志 → 产生日志第1行
- 调用CheckProcess,往sb加第二行「进程运行数量」,把sb全部2行内容写入日志 → 产生日志第2-3行
- CheckProcess判断后往sb加第三行「进程已运行」,把sb全部3行内容写入日志 → 产生日志第4-6行
- OnStart结尾往sb加第四行「Timer已设置」,把sb全部4行内容写入日志 → 产生日志第7-10行
总共1+2+3+4=10行内容,和你贴出的日志行数、内容完全匹配。你贴出的日志所有条目的时间戳完全相同,也直接印证了不存在跨时间的多线程执行场景,所有重复内容都是同一时刻写入的历史日志。
- 定时器无触发逻辑,导致程序“挂起”
你初始化Timer实例后,从未给Elapsed事件绑定任何回调方法:- 你虽然定义了
OnElapsedTime方法,但没有写timer.Elapsed += OnElapsedTime的绑定逻辑,定时器走完30秒间隔后找不到要执行的代码,自然不会产生任何新日志,表现为程序挂起。 - 就算完成了事件绑定,当前
OnElapsedTime方法里根本没有调用CheckProcess,触发后也不会执行进程检测逻辑。
- 你虽然定义了
- 默认定时器机制和预期执行流程不匹配
你使用的System.Timers.Timer默认开启AutoReset = true,触发逻辑是时间到了就立即触发事件,不管上一次的事件处理是否执行完成,天然存在多线程并发执行的风险,完全不符合你要的「单次检测执行完成后,再等待30秒启动下一次检测」的串行执行要求。且你没有任何重入保护,一旦绑定事件,很容易出现多线程同时检测、重复拉起进程的问题。
正确修复方向
- 修正日志写入逻辑:不要依赖累积的StringBuilder重复写历史内容,每次生成单条日志时即时写入文件,或在写完日志后主动清空StringBuilder,避免重复输出和内存累积。
- 适配串行执行的定时器逻辑:
- 将定时器
AutoReset属性设为false,即单次触发后自动停止计时 - 把
CheckProcess方法作为定时器的回调绑定到Elapsed事件上 - 当
CheckProcess执行完成后,再手动启动定时器开始下一次30秒倒计时,实现“执行完再等待”的预期流程
- 将定时器
- 增加重入保护:使用原子操作标记当前检测逻辑是否在运行,若执行未完成则直接跳过本次触发,从机制上杜绝并发执行的风险。
- 完善停止逻辑:在
OnStop事件中先停止定时器、释放资源,再置空引用,避免出现对象已释放的异常。
内容的提问来源于stack exchange,提问作者Mick
相关产品推荐
相关产品推荐

