树莓派Linux Mono环境下,如何检测系统时间变化修复WinForms Timer暂停问题
解决Mono环境下System.Windows.Forms.Timer因系统时间校正停止的问题
问题原因
System.Windows.Forms.Timer是依赖WinForms消息循环的定时器,它通过计算绝对系统时间来触发Tick事件。当NTP将快进的系统时间回拨校正时,定时器会认为下一次触发的时间还未到来,因此会暂停触发,直到系统时间走到原本预期的“未来时间”才恢复。
解决方案
方案1:替换为不受绝对时间影响的定时器
使用System.Timers.Timer或System.Threading.Timer,这类定时器基于实际流逝的时间间隔触发,不受系统时间回拨的影响。注意:非WinForms定时器的触发事件在非UI线程,更新界面时需要通过Invoke切换到UI线程。
示例代码(替换为System.Timers.Timer):
private System.Timers.Timer _logTimer; public void Form1_Load(object sender, EventArgs e) { _logTimer = new System.Timers.Timer(100); _logTimer.Elapsed += LogTimer_Elapsed; _logTimer.Start(); } private void LogTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { // 切换到UI线程更新控件 this.Invoke((Action)(() => { lblTime.Text = DateTime.Now.ToString("dd-MM-yy HH:mm:ss"); })); } // 窗体关闭时释放资源 protected override void OnFormClosed(FormClosedEventArgs e) { _logTimer?.Dispose(); base.OnFormClosed(e); }
方案2:检测系统时间变化,重置原有Timer
通过后台定时检查系统时间,当检测到时间回拨时,手动重置System.Windows.Forms.Timer。这里用System.Threading.Timer来做时间检测,因为它不受系统时间变化影响,能稳定执行检查逻辑。
示例代码:
private DateTime _lastCheckedTime; private System.Threading.Timer _timeCheckTimer; public Form1() { InitializeComponent(); _lastCheckedTime = DateTime.Now; // 每隔1秒检查一次系统时间 _timeCheckTimer = new System.Threading.Timer(CheckSystemTimeChange, null, 0, 1000); } private void CheckSystemTimeChange(object state) { DateTime currentTime = DateTime.Now; // 检测时间回拨(允许1秒误差,避免系统时间精度问题) if (currentTime < _lastCheckedTime.AddSeconds(-1)) { // 在UI线程重置WinForms Timer this.Invoke((Action)(() => { LogTimer.Stop(); LogTimer.Start(); })); } _lastCheckedTime = currentTime; } // 窗体关闭时释放检测定时器 protected override void OnFormClosed(FormClosedEventArgs e) { _timeCheckTimer?.Dispose(); base.OnFormClosed(e); }
注意事项
- 若使用方案1,需确保所有UI操作都通过
Invoke或BeginInvoke切换到UI线程,避免跨线程访问控件的异常。 - 方案2的时间检测间隔可根据需求调整,1秒的间隔既能及时捕获时间变化,也不会占用过多系统资源。
内容的提问来源于stack exchange,提问作者pm101
相关产品推荐
相关产品推荐

