Reputation: 676
I am currently working on a side project where I have to process a lot of data regarding virtual machines using ManagementObject
to get information about each machine.
I want to be able to process each virtual machine I find in parallel, updating a progress bar after each iteration is complete (and therefore that virtual machine is done being processed). Currently I have a total of about 81 VMs. I have a simple button in WPF that fires this off:
ClusterCrawler testCrawler = new ClusterCrawler("MyCluster");
StartButton.IsEnabled = false;
List<RTClusterNode> clustNodeParallel = null;
clustNodeParallel = await Task.Run(()=>testCrawler.CrawlAndReturnNodesParrallelAsync(CrawlProgressBar));
Where CrawlProgressBar
is a ProgressBar
in the Main Window where the button is.
The CrawlAndReturnNodesParallelAsync
method exist inside my ClusterCrawler
class and processes a list of virtual machines, but at the end it uses a ParallelForEach
loop to speed up the processing:
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;
}
I feel like I am not using the Dispatcher
properly here, because even when I try to I still get exceptions that say that the thread cannot access this object (the progress bar) because it does not own it.
Any ideas as to how I can update the progress bar as the parallel for each loop executes?
Edit:
@Servy had the right answer for this. I also noticed an issue higher up in the code that I had not spotted until now. Higher up inside CrawlAndReturnNodesParallelAsync
, I had the following line:
pBar.Maximum = totalVms;
I did not notice this before because when I attach the debugger to the remote machine that I am running the code on, the debugger always times out before I get a chance to see exactly what line the code failed on. I've posted about this issue on here but have yet to get to the bottom of it.
Besides that, due to time constraints, I did a hybrid of my previous solution as well as @Servy's. My Button
code now includes this:
IProgress<int> progress = new Progress<int>(n => CrawlProgressBar.Value = n);
clustNodeParallel = await Task.Run(()=>testCrawler.CrawlAndReturnNodesParrallelAsync(progress, CrawlProgressBar));
And the actual method signature was changed to:
public List<RTClusterNode> CrawlAndReturnNodesParrallelAsync(IProgress<int> progress, ProgressBar pBar)
I used @Servy's solution here:
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);
}
});
But I also used my earlier solution that I had originally a few lines up to fix the issue when I was setting the maximum value for the ProgressBar
:
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate()
{
pBar.Maximum = totalVms;
});
Upvotes: 1
Views: 4282
Reputation: 203829
Use the Progress<T>
class to update the UI with the progress of a background task. It will take care of all of the UI marshaling on your behalf, so you don't need to explicitly do any of it.
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;
}
Upvotes: 1