【发布时间】:2020-05-05 08:01:40
【问题描述】:
我有一个 WinForm 应用程序,我正在尝试在并行循环中更新进度条。这是我的代码的 sn-p:
Parallel.ForEach(files, (file, state) =>
{
//Intialization of parameters
//do cpu-intensive task
DoWork();
UpdateProgress();
});
int counter = 0;
private object updateLock = new object();
void UpdateProgress()
{
lock (updateLock)
{
counter++;
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(() => { progressBar1.SetProgress(counter); });
}
else
{
progressBar1.SetProgress(counter);
}
}
}
为了获得进度条动画的即时更新,我使用了SetProgress。
public static void SetProgress(this ProgressBar bar, int value)
{
if (value == bar.Maximum)
{
bar.Maximum = value + 1;
bar.Value = value + 1;
bar.Maximum = value;
}
else
{
bar.Value = value + 1;
}
bar.Value = value;
}
整个过程似乎运行良好,但我对进度条的更新方式有疑问。随机我看到进度动画来回设置,例如转到 33/150,然后到 31/150,然后到 32/150。虽然我使用同步锁定对象来相应地更新每个步骤的进度,但主 UI 线程中的消息似乎没有按顺序处理,或者代码有问题。
任何想法可能是什么问题?
提前致谢。
[更新]
【问题讨论】:
-
我强烈建议将进度条更新调用移到 lock 语句之外。目前,您在长期进度条更新期间阻止计数器增加(UI 线程切换!)。锁定区域应尽可能短。根据您的
DoWork方法需要多长时间,更新进度条的UI 线程切换可能需要更长时间。有时最好在例如轮询计数器。使用计时器间隔 300 毫秒,然后更新进度条。你也可以看看快速Interlocked.Increment方法。 -
@KBO 感谢您的建议,但它与这种行为有什么关系吗?我假设通过将进度条更新调用移出锁定区域,竞争条件会大大增加。在通过锁定进行长期进度条更新期间的当前计数器增加中,似乎每次运行中的某个随机点来回设置进度。
-
您还分配了两次
bar.Maximum,然后进度条最大值小于该值。这也可能导致这种行为。您应该删除行bar.Maximum = value; -
@KBO "根据你的 DoWork 方法需要多长时间,UI 线程切换更新进度条可能需要更长的时间",这个更长的更新是否意味着可能会更早地观察到下一个进度动画更新上一个?
-
在调用
SetProgress之前尝试记录counter的值。它将帮助您查看进度条的更新是否不按 FIFO 顺序,或者其他一些奇怪的东西。
标签: c# .net progress-bar task-parallel-library