fix: 修复长时间运行后的卡死问题

This commit is contained in:
2026-01-19 20:56:07 +08:00
parent d421b9b72b
commit 37bef1ead3
3 changed files with 132 additions and 71 deletions

View File

@@ -13,7 +13,8 @@ namespace TimerApp
public sealed class ActivityMonitor : IDisposable 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 TimeSpan _accumulatedWorkTime;
private int _restElapsedSeconds; private int _restElapsedSeconds;
private bool _isPaused; private bool _isPaused;
@@ -31,7 +32,11 @@ namespace TimerApp
public TimeSpan IdleThreshold { get; set; } = TimeSpan.FromSeconds(30); public TimeSpan IdleThreshold { get; set; } = TimeSpan.FromSeconds(30);
public MonitorState CurrentState { get; private set; } = MonitorState.Idle; 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<TimeSpan>? WorkProgressChanged; // 剩余工作时间 public event EventHandler<TimeSpan>? WorkProgressChanged; // 剩余工作时间
@@ -42,34 +47,54 @@ namespace TimerApp
public ActivityMonitor() public ActivityMonitor()
{ {
_timer = new System.Windows.Forms.Timer();
_timer.Interval = 1000; // 1秒检查一次
_timer.Tick += Timer_Tick;
SystemEvents.PowerModeChanged += OnPowerModeChanged; SystemEvents.PowerModeChanged += OnPowerModeChanged;
} }
public void Start() public void Start()
{ {
lock (_lock)
{
StopInternal();
_cts = new CancellationTokenSource();
var token = _cts.Token;
// 启动时立即触发一次检测 // 启动时立即触发一次检测
_checkTickCounter = CheckIntervalTicks; _checkTickCounter = CheckIntervalTicks;
_timer.Start();
ResetWork(); ResetWork();
Task.Run(() => MonitorLoop(token), token);
}
} }
public void Stop() public void Stop()
{ {
_timer.Stop(); lock (_lock)
{
StopInternal();
}
}
private void StopInternal()
{
if (_cts != null)
{
_cts.Cancel();
_cts.Dispose();
_cts = null;
}
} }
private void ResetWork() private void ResetWork()
{ {
// Must be called within lock
_accumulatedWorkTime = TimeSpan.Zero; _accumulatedWorkTime = TimeSpan.Zero;
ChangeState(MonitorState.Idle); ChangeState(MonitorState.Idle);
} }
private void ChangeState(MonitorState newState) private void ChangeState(MonitorState newState)
{ {
// Must be called within lock
if (CurrentState != newState) if (CurrentState != newState)
{ {
CurrentState = newState; CurrentState = newState;
@@ -77,9 +102,29 @@ namespace TimerApp
} }
} }
private void Timer_Tick(object? sender, EventArgs e) private async Task MonitorLoop(CancellationToken token)
{
while (!token.IsCancellationRequested)
{ {
try 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) if (_isPaused)
@@ -134,15 +179,12 @@ namespace TimerApp
// 用户离开了或正在播放视频 // 用户离开了或正在播放视频
if (CurrentState == MonitorState.Working) if (CurrentState == MonitorState.Working)
{ {
// 如果正在工作,但离开了,暂停工作计时? // 如果空闲时间超过阈值,状态变为空闲
// 简单起见,如果离开时间过长,可以视为一种“休息”,或者只是暂停累积
// 这里我们简单处理:如果空闲时间超过阈值,状态变为空闲
ChangeState(MonitorState.Idle); ChangeState(MonitorState.Idle);
} }
// 如果在 Idle 状态,且空闲时间非常长(比如超过了休息时间), // 如果在 Idle 状态,且空闲时间非常长(比如超过了休息时间),
// 是否应该重置工作计时器 // 重置工作计时器
// 假设用户去开会了1小时回来应该重新计算20分钟。
if (idleTime > RestDuration) if (idleTime > RestDuration)
{ {
_accumulatedWorkTime = TimeSpan.Zero; _accumulatedWorkTime = TimeSpan.Zero;
@@ -151,7 +193,6 @@ namespace TimerApp
// 如果正在播放视频,不累加工作时间,但保持当前状态 // 如果正在播放视频,不累加工作时间,但保持当前状态
if (isMediaPlaying && CurrentState == MonitorState.Working) if (isMediaPlaying && CurrentState == MonitorState.Working)
{ {
// 保持当前剩余时间不变(不累加,也不减少)
TimeSpan remainingWork = WorkDuration - _accumulatedWorkTime; TimeSpan remainingWork = WorkDuration - _accumulatedWorkTime;
WorkProgressChanged?.Invoke(this, remainingWork); WorkProgressChanged?.Invoke(this, remainingWork);
} }
@@ -166,7 +207,6 @@ namespace TimerApp
} }
// 累加工作时间 // 累加工作时间
// 简单的累加逻辑:这一秒是工作的
_accumulatedWorkTime += TimeSpan.FromSeconds(1); _accumulatedWorkTime += TimeSpan.FromSeconds(1);
// 检查是否达到工作时长 // 检查是否达到工作时长
@@ -186,13 +226,11 @@ namespace TimerApp
} }
} }
} }
catch (Exception ex)
{
Logger.LogError("Error in ActivityMonitor Timer_Tick", ex);
}
} }
public void RefreshStatus() public void RefreshStatus()
{
lock (_lock)
{ {
if (CurrentState == MonitorState.Working) if (CurrentState == MonitorState.Working)
{ {
@@ -201,7 +239,6 @@ namespace TimerApp
} }
else if (CurrentState == MonitorState.Resting) else if (CurrentState == MonitorState.Resting)
{ {
// 使用计数器计算剩余时间,保持一致性
int totalRestSeconds = (int)RestDuration.TotalSeconds; int totalRestSeconds = (int)RestDuration.TotalSeconds;
int remainingSeconds = totalRestSeconds - _restElapsedSeconds; int remainingSeconds = totalRestSeconds - _restElapsedSeconds;
if (remainingSeconds < 0) remainingSeconds = 0; if (remainingSeconds < 0) remainingSeconds = 0;
@@ -209,15 +246,21 @@ namespace TimerApp
RestProgressChanged?.Invoke(this, remaining); RestProgressChanged?.Invoke(this, remaining);
} }
} }
}
public void Restart() public void Restart()
{
lock (_lock)
{ {
_accumulatedWorkTime = TimeSpan.Zero; _accumulatedWorkTime = TimeSpan.Zero;
_isPaused = false; _isPaused = false;
IsPaused = false;
// Ensure timer is running // Ensure task is running
if (!_timer.Enabled) _timer.Start(); if (_cts == null || _cts.IsCancellationRequested)
{
// Re-start if stopped (though Restart implies it's running)
// Usually Start() calls ResetWork, so we just reset here
}
// Force state to Working since user manually restarted // Force state to Working since user manually restarted
ChangeState(MonitorState.Working); ChangeState(MonitorState.Working);
@@ -225,29 +268,32 @@ namespace TimerApp
// Immediately refresh UI to show full duration // Immediately refresh UI to show full duration
RefreshStatus(); RefreshStatus();
} }
}
public void Pause() public void Pause()
{
lock (_lock)
{ {
if (!_isPaused && CurrentState != MonitorState.Idle) if (!_isPaused && CurrentState != MonitorState.Idle)
{ {
_isPaused = true; _isPaused = true;
IsPaused = true;
StateChanged?.Invoke(this, EventArgs.Empty); StateChanged?.Invoke(this, EventArgs.Empty);
} }
} }
}
public void Resume() public void Resume()
{
lock (_lock)
{ {
if (_isPaused) if (_isPaused)
{ {
_isPaused = false; _isPaused = false;
IsPaused = false;
StateChanged?.Invoke(this, EventArgs.Empty); StateChanged?.Invoke(this, EventArgs.Empty);
RefreshStatus(); RefreshStatus();
} }
} }
}
public void Dispose() public void Dispose()
{ {
@@ -255,10 +301,7 @@ namespace TimerApp
_disposed = true; _disposed = true;
SystemEvents.PowerModeChanged -= OnPowerModeChanged; SystemEvents.PowerModeChanged -= OnPowerModeChanged;
Stop();
_timer.Stop();
_timer.Tick -= Timer_Tick;
_timer.Dispose();
} }
private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e) private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)

View File

@@ -412,7 +412,7 @@ namespace TimerApp
{ {
if (InvokeRequired) if (InvokeRequired)
{ {
Invoke(new Action<object?, EventArgs>(Monitor_StateChanged), sender, e); BeginInvoke(new Action<object?, EventArgs>(Monitor_StateChanged), sender, e);
return; return;
} }
UpdateStatusUI(); UpdateStatusUI();
@@ -461,19 +461,25 @@ namespace TimerApp
{ {
if (InvokeRequired) if (InvokeRequired)
{ {
Invoke(new Action<object?, TimeSpan>(Monitor_WorkProgressChanged), sender, remaining); BeginInvoke(new Action<object?, TimeSpan>(Monitor_WorkProgressChanged), sender, remaining);
return; return;
} }
lblTimeLeft.Text = $"{remaining.Minutes:D2}:{remaining.Seconds:D2}"; lblTimeLeft.Text = $"{remaining.Minutes:D2}:{remaining.Seconds:D2}";
// Update tray tooltip // Update tray tooltip
string newText;
if (remaining.TotalMinutes < 1) if (remaining.TotalMinutes < 1)
{ {
notifyIcon1.Text = $"即将休息: {remaining.Seconds}秒"; newText = $"即将休息: {remaining.Seconds}秒";
} }
else else
{ {
notifyIcon1.Text = $"工作中: 剩余 {remaining.Minutes} 分钟"; newText = $"工作中: 剩余 {remaining.Minutes} 分钟";
}
if (notifyIcon1.Text != newText)
{
notifyIcon1.Text = newText;
} }
} }
@@ -481,7 +487,7 @@ namespace TimerApp
{ {
if (InvokeRequired) if (InvokeRequired)
{ {
Invoke(new Action<object?, EventArgs>(Monitor_RestStarted), sender, e); BeginInvoke(new Action<object?, EventArgs>(Monitor_RestStarted), sender, e);
return; return;
} }
@@ -503,6 +509,12 @@ namespace TimerApp
private void Monitor_RestProgressChanged(object? sender, TimeSpan remaining) private void Monitor_RestProgressChanged(object? sender, TimeSpan remaining)
{ {
if (InvokeRequired)
{
BeginInvoke(new Action<object?, TimeSpan>(Monitor_RestProgressChanged), sender, remaining);
return;
}
if (_restForm != null && !_restForm.IsDisposed && _restForm.Visible) if (_restForm != null && !_restForm.IsDisposed && _restForm.Visible)
{ {
_restForm.UpdateTime(remaining); _restForm.UpdateTime(remaining);
@@ -513,7 +525,7 @@ namespace TimerApp
{ {
if (InvokeRequired) if (InvokeRequired)
{ {
Invoke(new Action<object?, EventArgs>(Monitor_RestEnded), sender, e); BeginInvoke(new Action<object?, EventArgs>(Monitor_RestEnded), sender, e);
return; return;
} }
@@ -568,9 +580,15 @@ namespace TimerApp
private void ShowForm() private void ShowForm()
{ {
if (this.IsDisposed) return;
this.Show(); this.Show();
if (this.WindowState == FormWindowState.Minimized)
{
this.WindowState = FormWindowState.Normal; this.WindowState = FormWindowState.Normal;
}
this.Activate(); this.Activate();
this.BringToFront();
} }
public void ActivateFromExternal() public void ActivateFromExternal()

View File

@@ -143,7 +143,7 @@ namespace TimerApp
{ {
if (lblTimer.InvokeRequired) if (lblTimer.InvokeRequired)
{ {
lblTimer.Invoke(new Action<TimeSpan>(UpdateTime), remaining); lblTimer.BeginInvoke(new Action<TimeSpan>(UpdateTime), remaining);
} }
else else
{ {