WPF 应用程序,“时钟文本框”在几个小时后冻结
Posted
技术标签:
【中文标题】WPF 应用程序,“时钟文本框”在几个小时后冻结【英文标题】:WPF application, "clock textbox" freezes after some hours 【发布时间】:2021-06-23 02:43:41 【问题描述】:我制作了一个简单的WPF
全屏并始终在顶部窗口,它有一个带有dddd dd MMMM yyyy
HH:mm:ss
时间戳的时钟。该应用程序在始终在触摸屏上打开 24/24,问题是 一段时间后我看到时钟在某个时间冻结。应用程序本身并没有冻结,因为我放了一些按钮,它完全可以工作。
如何更新时钟文本框
在所有版本的时钟中,我都有同样的问题!
1。任务版本
private Task _clock;
private void LoadClock()
_clockCancellationTokenSource = new CancellationTokenSource();
_clock = Task.Run(async () =>
try
while (!_clockCancellationTokenSource.IsCancellationRequested)
DateTime now = DateTime.Now;
_ = Dispatcher.InvokeAsync(() =>
txtHour.Text = DateTime.Now.ToString("HH:mm:ss", CultureInfo.CurrentCulture);
txtDate.Text = now.ToString("dddd dd MMMM yyyy", CultureInfo.CurrentCulture);
,
DispatcherPriority.Normal);
await Task.Delay(800, _clockCancellationTokenSource.Token);
catch
// I don't do anything with the errors here, If any
, _clockCancellationTokenSource.Token);
在Page_Unloaded
事件中:
if (_clock != null && !_clock.IsCompleted)
_clockCancellationTokenSource?.Cancel();
_clock.Wait();
_clock.Dispose();
_clockCancellationTokenSource?.Dispose();
2。 DispatcherTimer 版本
private DispatcherTimer _clock;
private void LoadClock(bool show)
_clock = new DispatcherTimer
Interval = TimeSpan.FromSeconds(1)
;
_clockTimer_Tick(null, null);
_clock.Tick += _clockTimer_Tick;
_clock.Start();
private void _clockTimer_Tick(object sender, object e)
var now = DateTime.Now;
txtHour.Text = DateTime.Now.ToString("HH:mm:ss", CultureInfo.CurrentCulture);
txtDate.Text = now.ToString("dddd dd MMMM yyyy", CultureInfo.CurrentCulture);
在Page_Unloaded
事件中:
if (_clock != null)
_clock.Stop();
_clock.Tick -= _clockTimer_Tick;
3。 System.Timers.Timer 版本
private Timer _clock;
private void LoadClock(bool show)
// Timer per gestire l'orario
_clock = new Timer(800);
_clock.Elapsed += _clock_Elapsed;
_clock.Start();
_clock_Elapsed(null, null);
private void _clock_Elapsed(object sender, ElapsedEventArgs e)
DateTime now = DateTime.Now;
Dispatcher.Invoke(() =>
txtHour.Text = DateTime.Now.ToString("HH:mm:ss", CultureInfo.CurrentCulture);
txtDate.Text = now.ToString("dddd dd MMMM yyyy", CultureInfo.CurrentCulture);
);
在Page_Unloaded
事件中:
_clock.Stop();
_clock.Elapsed -= _clock_Elapsed;
_clock.Dispose();
到目前为止我所尝试的:
如您从我的代码中看到的那样更改计时器实现 在定时器tick
回调中添加日志以查看它是否由于UI调度问题或定时器问题而停止。有趣的是,我看到计时器在一定时间后停止计时。
我认为这可能是 Windows 的问题,可能会停止额外的线程,但我没有找到任何线索!
你有什么建议吗?
【问题讨论】:
您的问题是您缺少异常。永远不要吞下异常。添加一些适当的错误处理代码。 另外,您不需要使用Task.Run
或Invoke
,因为 WPF 会为您添加自己的同步上下文。
延迟 800 毫秒会给你一个 janky UI。您应该使用 500 毫秒,或 1 秒的某个整数部分。 500ms 是您可以使用的最高延迟(和最低频率)。你需要阅读Nyquist frequency
【参考方案1】:
问题是您有一个try/catch
,它在while
循环之外,而您的代码只是在吞噬异常-因此,当抛出异常时,它将停止循环而无法恢复它。
至少,您需要对您的第一个代码示例进行 2 处更改以使其可靠地工作:
您需要捕获异常并优雅地处理它们。永远不要默默吞下异常。 将try/catch
移动到内部while
循环。
此外,正如我在 cmets 中所说,您发布的代码过于复杂:
您不需要使用Task.Run
或Dispatcher.InvokeAsync
,因为WPF 设置了自己的SynchronizationContext
,这确保await
将在默认情况下在UI 线程中恢复(除非您使用ConfigureAwait(false)
,课程)
所以永远不要在 UI 代码中使用ConfigureAwait(false)
!)
您的 Task.Delay
超时 800ms
的时钟以秒为单位显示时间将导致更新不稳定和尴尬(因为在 800 毫秒时,更新将在 800、1600、2400、3200 等处) t 与挂钟秒数对齐)。
每当您制作连续值的离散样本时(在这种情况下:秒),那么您应该使用信号源的奈奎斯特频率(它是预期频率的两倍:所以要获得 1 秒的样本,您应该每 500 毫秒采样一次 - 尽管对于时间显示的情况,我更喜欢 100 毫秒的间隔来解释 UI 调度程序的抖动。如果你确实走得更高(例如 60 fps 为 16 毫秒),你应该只在秒值发生变化时更新 UI,否则会浪费 CPU 和 GPU 周期)。
你只需要这个:
private bool clockEnabled = false;
private readonly Task clockTask;
public MyPage()
this.clockTask = this.RunClockAsync( default );
private override void OnLoad( ... )
this.clockEnabled = true;
private async Task RunClockAsync( CancellationToken cancellationToken = default )
while( !cancellationToken.IsCancellationRequested )
try
if( this.clockEnabled )
// WPF will always run this code in the UI thread:
this.UpdateClockDisplay();
await Task.Delay( 100 ); // No need to pass `cancellationToken` to `Task.Delay` as we check `IsCancellationRequested ` ourselves.
catch( OperationCanceledException )
// This catch block only needs to exist if the cancellation token is passed-around inside the try block.
return;
catch( Exception ex )
this.log.LogError( ex );
MessageBox.Show( "Error: " + ex.ToString(), etc... );
private void UpdateClockDisplay()
DateTime now = DateTime.Now;
// Don't cache CurrentCulture, because the user can change their system preferences at any time.
CultureInfo ci = CultureInfo.CurrentCulture;
this.txtHour.Text = now.ToString( "HH:mm:ss", ci );
this.txtDate.Text = now.ToString( "dddd dd MMMM yyyy", ci );
【讨论】:
我去试试看告诉你,谢谢! 我不敢相信我没有注意到我将 try..catch 放在循环之外。也非常感谢其他建议!以上是关于WPF 应用程序,“时钟文本框”在几个小时后冻结的主要内容,如果未能解决你的问题,请参考以下文章