2015-08-04 4 views
0

В настоящее время я работаю над побочным проектом, где мне нужно обработать много данных о виртуальных машинах, используя ManagementObject, чтобы получить информацию о каждой машине.Обновление пользовательского интерфейса от Parallel ForEach

Я хочу иметь возможность обрабатывать каждую виртуальную машину, которую я нахожу параллельно, обновляя индикатор выполнения после завершения каждой итерации (и, следовательно, эта виртуальная машина обрабатывается). В настоящее время я имею в общей сложности около 81 виртуальных машин. У меня есть простая кнопка в 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 здесь, потому что даже когда я пытаюсь получить все исключения, которые говорят, что поток не может получить доступ к этому объекту (индикатор выполнения), потому что он не владеет им.

Любые идеи относительно того, как я могу обновить индикатор выполнения, поскольку параллель для каждого цикла выполняется?

Edit:

@Servy был правильный ответ на это. Я также заметил проблему выше в коде, который я не заметил до сих пор. Выше в CrawlAndReturnNodesParallelAsync, я имел следующую строку:

pBar.Maximum = totalVms; 

я не заметил этого раньше, потому что, когда я прикрепить отладчик к удаленной машине, что я бегу код на, отладчик всегда раз, прежде чем я получаю возможность увидеть, в какой строке код сработал. Я опубликовал эту проблему здесь, но пока не понял ее.

Кроме того, из-за ограничений по времени, я сделал гибрид своего предыдущего решения, а также @ Servy's. Мой 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; 
}); 

ответ

1

Используйте класс Progress<T> для обновления пользовательского интерфейса с выполнением фоновой задачи. Он позаботится обо всех маршлингах 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; 
} 
+0

Хмм, попробовал ваше решение, по-прежнему получаю исключение, заявляя, что вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток. Это потому, что я пытаюсь использовать Task.Запустить в начале кода кнопки? – user2357446

+0

@ user2357446 Да, это вызовет эту проблему. Если вы создаете экземпляр 'Progress' вне вызова' Task.Run' (и передаете его методу вместо строки выполнения), тогда это позаботится об этом. Также обратите внимание, что вы пометили этот метод как 'async', но, похоже, обеспечили синхронную реализацию. Если он выполняет свою работу синхронно, он не должен быть помечен как 'async'. – Servy

+0

Я его обозначил как «async», потому что без него попытка вернуть только «clusterNodes_hr» в конце метода приводит к ошибке компилятора, так как теперь она пытается вернуть «Задачу» вместо «Список 'как будто мне это нужно. – user2357446

Смежные вопросы