Updating UI from Parallel ForEach
本文关键字:ForEach Parallel from UI Updating | 更新日期: 2023-09-27 18:06:15
我目前正在做一个业余项目,我必须使用ManagementObject
处理大量关于虚拟机的数据,以获取每台机器的信息。
我希望能够并行处理我找到的每个虚拟机,在每次迭代完成后更新进度条(因此虚拟机已经完成处理)。目前我总共有81个vm。我在WPF中有一个简单的按钮来触发它:
ClusterCrawler testCrawler = new ClusterCrawler("MyCluster");
StartButton.IsEnabled = false;
List<RTClusterNode> clustNodeParallel = null;
clustNodeParallel = await Task.Run(()=>testCrawler.CrawlAndReturnNodesParrallelAsync(CrawlProgressBar));
其中CrawlProgressBar
为按钮所在的主窗口中的ProgressBar
。
CrawlAndReturnNodesParallelAsync
方法存在于我的ClusterCrawler
类中,并处理虚拟机列表,但最后它使用ParallelForEach
循环来加速处理:
public async Task<List<RTClusterNode>> CrawlAndReturnNodesParrallelAsync(ProgressBar pBar)
{
//Some processing done here
int count = 0;
Parallel.ForEach(clusterNodes_hr, node =>
{
foreach (var vm in node.VmList)
{
Console.WriteLine("Crawling VM: " + vm.VmName);
VirtualMachineCrawler vmCrawler = new VirtualMachineCrawler(vm);
vmCrawler.CrawlForDataDiskSpace();
Interlocked.Increment(ref count);
Console.WriteLine("Current count: " + count);
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate(){
pBar.Value = count;
});
}
});
return clusterNodes_hr;
}
我觉得我没有使用Dispatcher
正确在这里,因为即使当我尝试我仍然得到异常,说线程不能访问这个对象(进度条),因为它不拥有它。
关于如何在每个循环执行并行时更新进度条的任何想法?
编辑:@Servy有正确的答案。我还注意到代码中更高层的一个问题,直到现在才发现。在
CrawlAndReturnNodesParallelAsync
内部,我有以下行:
pBar.Maximum = totalVms;
我之前没有注意到这一点,因为当我将调试器连接到我正在运行代码的远程机器时,调试器总是在我有机会确切地看到代码在哪一行失败之前超时。我已经在这里发布了这个问题,但还没有找到它的底部。
除此之外,由于时间限制,我将我之前的解决方案和@Servy的解决方案混合在一起。我的Button
代码现在包含以下内容:
IProgress<int> progress = new Progress<int>(n => CrawlProgressBar.Value = n);
clustNodeParallel = await Task.Run(()=>testCrawler.CrawlAndReturnNodesParrallelAsync(progress, CrawlProgressBar));
实际的方法签名改为:
public List<RTClusterNode> CrawlAndReturnNodesParrallelAsync(IProgress<int> progress, ProgressBar pBar)
我在这里使用了@Servy的解决方案:
Parallel.ForEach(clusterNodes_hr, node =>
{
foreach (var vm in node.VmList)
{
Console.WriteLine("Crawling VM: " + vm.VmName);
VirtualMachineCrawler vmCrawler = new VirtualMachineCrawler(vm);
vmCrawler.CrawlForDataDiskSpace();
Interlocked.Increment(ref count);
Console.WriteLine("Current count: " + count);
progress.Report(count);
}
});
但我也使用了我以前的解决方案,我本来有几行来解决问题,当我设置ProgressBar
的最大值:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate()
{
pBar.Maximum = totalVms;
});
使用Progress<T>
类更新UI与后台任务的进度。它会代表你处理所有的UI封送,所以你不需要显式地做任何事情。
public async Task<List<RTClusterNode>> CrawlAndReturnNodesParrallelAsync(ProgressBar pBar)
{
IProgress<int> progress = new Progress<int>(n => pBar.Value = n);
//Some processing done here
int count = 0;
Parallel.ForEach(clusterNodes_hr, node =>
{
foreach (var vm in node.VmList)
{
Console.WriteLine("Crawling VM: " + vm.VmName);
VirtualMachineCrawler vmCrawler = new VirtualMachineCrawler(vm);
vmCrawler.CrawlForDataDiskSpace();
Interlocked.Increment(ref count);
Console.WriteLine("Current count: " + count);
progress.Report(count);
}
});
return clusterNodes_hr;
}