From 37bef1ead38d09b5d572d5a1db76517547d2829b Mon Sep 17 00:00:00 2001 From: Solin Date: Mon, 19 Jan 2026 20:56:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=95=BF=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E8=BF=90=E8=A1=8C=E5=90=8E=E7=9A=84=E5=8D=A1=E6=AD=BB?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ActivityMonitor.cs | 169 ++++++++++++++++++++++++++++----------------- MainForm.cs | 32 +++++++-- RestForm.cs | 2 +- 3 files changed, 132 insertions(+), 71 deletions(-) diff --git a/ActivityMonitor.cs b/ActivityMonitor.cs index 615b2bf..ead9e7c 100644 --- a/ActivityMonitor.cs +++ b/ActivityMonitor.cs @@ -13,7 +13,8 @@ namespace TimerApp public sealed class ActivityMonitor : IDisposable { - private readonly System.Windows.Forms.Timer _timer; + private CancellationTokenSource? _cts; + private readonly object _lock = new object(); private TimeSpan _accumulatedWorkTime; private int _restElapsedSeconds; private bool _isPaused; @@ -31,7 +32,11 @@ namespace TimerApp public TimeSpan IdleThreshold { get; set; } = TimeSpan.FromSeconds(30); public MonitorState CurrentState { get; private set; } = MonitorState.Idle; - public bool IsPaused { get; private set; } = false; + public bool IsPaused + { + get { lock (_lock) return _isPaused; } + private set { lock (_lock) _isPaused = value; } + } // 事件 public event EventHandler? WorkProgressChanged; // 剩余工作时间 @@ -42,34 +47,54 @@ namespace TimerApp public ActivityMonitor() { - _timer = new System.Windows.Forms.Timer(); - _timer.Interval = 1000; // 1秒检查一次 - _timer.Tick += Timer_Tick; - SystemEvents.PowerModeChanged += OnPowerModeChanged; } public void Start() { - // 启动时立即触发一次检测 - _checkTickCounter = CheckIntervalTicks; - _timer.Start(); - ResetWork(); + lock (_lock) + { + StopInternal(); + + _cts = new CancellationTokenSource(); + var token = _cts.Token; + + // 启动时立即触发一次检测 + _checkTickCounter = CheckIntervalTicks; + ResetWork(); + + Task.Run(() => MonitorLoop(token), token); + } } public void Stop() { - _timer.Stop(); + lock (_lock) + { + StopInternal(); + } + } + + private void StopInternal() + { + if (_cts != null) + { + _cts.Cancel(); + _cts.Dispose(); + _cts = null; + } } private void ResetWork() { + // Must be called within lock _accumulatedWorkTime = TimeSpan.Zero; ChangeState(MonitorState.Idle); } private void ChangeState(MonitorState newState) { + // Must be called within lock if (CurrentState != newState) { CurrentState = newState; @@ -77,9 +102,29 @@ namespace TimerApp } } - private void Timer_Tick(object? sender, EventArgs e) + private async Task MonitorLoop(CancellationToken token) { - try + while (!token.IsCancellationRequested) + { + try + { + await Task.Delay(1000, token); + OnTick(); + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + Logger.LogError("Error in ActivityMonitor MonitorLoop", ex); + } + } + } + + private void OnTick() + { + lock (_lock) { // 如果处于暂停状态,不处理计时逻辑 if (_isPaused) @@ -134,15 +179,12 @@ namespace TimerApp // 用户离开了或正在播放视频 if (CurrentState == MonitorState.Working) { - // 如果正在工作,但离开了,暂停工作计时? - // 简单起见,如果离开时间过长,可以视为一种“休息”,或者只是暂停累积 - // 这里我们简单处理:如果空闲时间超过阈值,状态变为空闲 + // 如果空闲时间超过阈值,状态变为空闲 ChangeState(MonitorState.Idle); } // 如果在 Idle 状态,且空闲时间非常长(比如超过了休息时间), - // 是否应该重置工作计时器? - // 假设用户去开会了1小时,回来应该重新计算20分钟。 + // 重置工作计时器 if (idleTime > RestDuration) { _accumulatedWorkTime = TimeSpan.Zero; @@ -151,7 +193,6 @@ namespace TimerApp // 如果正在播放视频,不累加工作时间,但保持当前状态 if (isMediaPlaying && CurrentState == MonitorState.Working) { - // 保持当前剩余时间不变(不累加,也不减少) TimeSpan remainingWork = WorkDuration - _accumulatedWorkTime; WorkProgressChanged?.Invoke(this, remainingWork); } @@ -166,7 +207,6 @@ namespace TimerApp } // 累加工作时间 - // 简单的累加逻辑:这一秒是工作的 _accumulatedWorkTime += TimeSpan.FromSeconds(1); // 检查是否达到工作时长 @@ -186,66 +226,72 @@ namespace TimerApp } } } - catch (Exception ex) - { - Logger.LogError("Error in ActivityMonitor Timer_Tick", ex); - } } public void RefreshStatus() { - if (CurrentState == MonitorState.Working) - { - TimeSpan remaining = WorkDuration - _accumulatedWorkTime; - WorkProgressChanged?.Invoke(this, remaining); - } - else if (CurrentState == MonitorState.Resting) - { - // 使用计数器计算剩余时间,保持一致性 - int totalRestSeconds = (int)RestDuration.TotalSeconds; - int remainingSeconds = totalRestSeconds - _restElapsedSeconds; - if (remainingSeconds < 0) remainingSeconds = 0; - TimeSpan remaining = TimeSpan.FromSeconds(remainingSeconds); - RestProgressChanged?.Invoke(this, remaining); - } + lock (_lock) + { + if (CurrentState == MonitorState.Working) + { + TimeSpan remaining = WorkDuration - _accumulatedWorkTime; + WorkProgressChanged?.Invoke(this, remaining); + } + else if (CurrentState == MonitorState.Resting) + { + int totalRestSeconds = (int)RestDuration.TotalSeconds; + int remainingSeconds = totalRestSeconds - _restElapsedSeconds; + if (remainingSeconds < 0) remainingSeconds = 0; + TimeSpan remaining = TimeSpan.FromSeconds(remainingSeconds); + RestProgressChanged?.Invoke(this, remaining); + } + } } public void Restart() { - _accumulatedWorkTime = TimeSpan.Zero; - _isPaused = false; - IsPaused = false; + lock (_lock) + { + _accumulatedWorkTime = TimeSpan.Zero; + _isPaused = false; + + // Ensure task is running + if (_cts == null || _cts.IsCancellationRequested) + { + // Re-start if stopped (though Restart implies it's running) + // Usually Start() calls ResetWork, so we just reset here + } - // Ensure timer is running - if (!_timer.Enabled) _timer.Start(); + // Force state to Working since user manually restarted + ChangeState(MonitorState.Working); - // Force state to Working since user manually restarted - ChangeState(MonitorState.Working); - - // Immediately refresh UI to show full duration - RefreshStatus(); + // Immediately refresh UI to show full duration + RefreshStatus(); + } } public void Pause() { - if (!_isPaused && CurrentState != MonitorState.Idle) + lock (_lock) { - _isPaused = true; - IsPaused = true; - - StateChanged?.Invoke(this, EventArgs.Empty); + if (!_isPaused && CurrentState != MonitorState.Idle) + { + _isPaused = true; + StateChanged?.Invoke(this, EventArgs.Empty); + } } } public void Resume() { - if (_isPaused) + lock (_lock) { - _isPaused = false; - IsPaused = false; - - StateChanged?.Invoke(this, EventArgs.Empty); - RefreshStatus(); + if (_isPaused) + { + _isPaused = false; + StateChanged?.Invoke(this, EventArgs.Empty); + RefreshStatus(); + } } } @@ -255,10 +301,7 @@ namespace TimerApp _disposed = true; SystemEvents.PowerModeChanged -= OnPowerModeChanged; - - _timer.Stop(); - _timer.Tick -= Timer_Tick; - _timer.Dispose(); + Stop(); } private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e) diff --git a/MainForm.cs b/MainForm.cs index 9080321..4467ac6 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -412,7 +412,7 @@ namespace TimerApp { if (InvokeRequired) { - Invoke(new Action(Monitor_StateChanged), sender, e); + BeginInvoke(new Action(Monitor_StateChanged), sender, e); return; } UpdateStatusUI(); @@ -461,19 +461,25 @@ namespace TimerApp { if (InvokeRequired) { - Invoke(new Action(Monitor_WorkProgressChanged), sender, remaining); + BeginInvoke(new Action(Monitor_WorkProgressChanged), sender, remaining); return; } lblTimeLeft.Text = $"{remaining.Minutes:D2}:{remaining.Seconds:D2}"; // Update tray tooltip + string newText; if (remaining.TotalMinutes < 1) { - notifyIcon1.Text = $"即将休息: {remaining.Seconds}秒"; + newText = $"即将休息: {remaining.Seconds}秒"; } else { - notifyIcon1.Text = $"工作中: 剩余 {remaining.Minutes} 分钟"; + newText = $"工作中: 剩余 {remaining.Minutes} 分钟"; + } + + if (notifyIcon1.Text != newText) + { + notifyIcon1.Text = newText; } } @@ -481,7 +487,7 @@ namespace TimerApp { if (InvokeRequired) { - Invoke(new Action(Monitor_RestStarted), sender, e); + BeginInvoke(new Action(Monitor_RestStarted), sender, e); return; } @@ -503,6 +509,12 @@ namespace TimerApp private void Monitor_RestProgressChanged(object? sender, TimeSpan remaining) { + if (InvokeRequired) + { + BeginInvoke(new Action(Monitor_RestProgressChanged), sender, remaining); + return; + } + if (_restForm != null && !_restForm.IsDisposed && _restForm.Visible) { _restForm.UpdateTime(remaining); @@ -513,7 +525,7 @@ namespace TimerApp { if (InvokeRequired) { - Invoke(new Action(Monitor_RestEnded), sender, e); + BeginInvoke(new Action(Monitor_RestEnded), sender, e); return; } @@ -568,9 +580,15 @@ namespace TimerApp private void ShowForm() { + if (this.IsDisposed) return; + this.Show(); - this.WindowState = FormWindowState.Normal; + if (this.WindowState == FormWindowState.Minimized) + { + this.WindowState = FormWindowState.Normal; + } this.Activate(); + this.BringToFront(); } public void ActivateFromExternal() diff --git a/RestForm.cs b/RestForm.cs index d54811a..fb219c1 100644 --- a/RestForm.cs +++ b/RestForm.cs @@ -143,7 +143,7 @@ namespace TimerApp { if (lblTimer.InvokeRequired) { - lblTimer.Invoke(new Action(UpdateTime), remaining); + lblTimer.BeginInvoke(new Action(UpdateTime), remaining); } else {