从 Parallel ForEach 更新 UI
Updating UI from Parallel ForEach
我目前正在做一个业余项目,我必须使用 ManagementObject
处理大量有关虚拟机的数据以获取有关每台机器的信息。
我希望能够并行处理我找到的每个虚拟机,在每次迭代完成后更新进度条(因此该虚拟机已完成处理)。目前我总共有大约 81 个虚拟机。我在 WPF 中有一个简单的按钮可以关闭它:
ClusterCrawler testCrawler = new ClusterCrawler("MyCluster");
StartButton.IsEnabled = false;
List<RTClusterNode> clustNodeParallel = null;
clustNodeParallel = await Task.Run(()=>testCrawler.CrawlAndReturnNodesParrallelAsync(CrawlProgressBar));
其中 CrawlProgressBar
是主要按钮 Window 中的 ProgressBar
。
CrawlAndReturnNodesParallelAsync
方法存在于我的 ClusterCrawler
class 中并处理虚拟机列表,但最后它使用 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>
class 更新后台任务的进度 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;
}
我目前正在做一个业余项目,我必须使用 ManagementObject
处理大量有关虚拟机的数据以获取有关每台机器的信息。
我希望能够并行处理我找到的每个虚拟机,在每次迭代完成后更新进度条(因此该虚拟机已完成处理)。目前我总共有大约 81 个虚拟机。我在 WPF 中有一个简单的按钮可以关闭它:
ClusterCrawler testCrawler = new ClusterCrawler("MyCluster");
StartButton.IsEnabled = false;
List<RTClusterNode> clustNodeParallel = null;
clustNodeParallel = await Task.Run(()=>testCrawler.CrawlAndReturnNodesParrallelAsync(CrawlProgressBar));
其中 CrawlProgressBar
是主要按钮 Window 中的 ProgressBar
。
CrawlAndReturnNodesParallelAsync
方法存在于我的 ClusterCrawler
class 中并处理虚拟机列表,但最后它使用 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>
class 更新后台任务的进度 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;
}