我遇到了一个(对我来说)很奇怪的问题。
我有一个C#/.net应用程序,其中我使用秒表来获得事件的准确计时。在计时期间,我想用进度更新标签。我的代码(简化):
private void backgroundWorker3_DoWork(object sender, DoWorkEventArgs e)
{
double stepDuration = Convert.ToDouble(textBoxPenetrationTime.Text);
Stopwatch clock = new Stopwatch();
long freq = Stopwatch.Frequency;
double ticksPerStep = (double)freq * stepDuration;
long preTick, postTick;
clock.Restart();
preTick = clock.ElapsedTicks;
while (clock.ElapsedTicks < ticksPerStep)
{
labelPercent.BeginInvoke((Action)delegate () { labelPercent.Text = Convert.ToInt32(clock.ElapsedTicks) / 1000000 * 100 / ((Convert.ToInt32(ticksPerStep) / 1000000)) + "%"; });
if (backgroundWorker3.CancellationPending == true)
{
return;
}
}
postTick = clock.ElapsedTicks;
}
当我运行start backgroundworker时,UI的其余部分(在另一个backgroundworker中处理的数据绘图和标签冻结,以及整个表单)挂起。
我尝试过的:
更新另一个backgroundworker中的标签并在while循环中调用该backgroundworker(结果相同)。在更新标签后尝试调用Application.DoEvents()(没有区别)
有一件事是可行的,这就是为什么我把这个问题描述为奇怪的原因,那就是如果我调用
Console.WriteLine(clock.ElapsedTicks);
在while循环的开始,一切都按照预期顺利运行。2我只是偶然发现的。
所以我的问题有两部分:
1.为什么UI在写入控制台时运行流畅,而在其他情况下则不然?
1.如何在不写入控制台的情况下解决此问题?
1条答案
按热度按时间11dmarpk1#
你需要减少在UI线程上安排工作的次数。每次调用Invoke时,你都会请求更新UI。如果你有一个紧密的循环,你可以轻松地调用Invoke每秒超过60次。除非你是一个游戏玩家和/或LTT的Linus,否则你不会看到/注意到你的标签的所有更新,即使它在每次调用时输出不同的值。
当你添加一个对Console.WriteLine的调用时,你基本上减少了UI线程上的负载,足以再次响应。这是因为Console.WriteLine是一个在CPU周期/ IO负载方面相对昂贵的调用。这是以你的后台任务现在需要更长时间才能完成为代价的。
我建议作以下修改:
ProgressChanged事件的实现
下面是您的DoWork在跟踪最后一个百分比时的样子,以便您可以在有理由这样做时调用ReportProgess。
如果你使用Timer每秒更新标签25次,你的DoWork看起来会是这样的。这是以额外的线程和更多的UI线程调用为代价的,但是你的工人循环没有进度问题。
下面是一个Gif的计时器解决方案: