Почему этот код Parallel.ForEach заморозит программу?

Другие вопросы новичка:

Этот код захватывает несколько прокси из списка в главном окне (я не мог понять, как сделать переменные доступными между различными функциями), и выполняет проверку каждого из них (просто httpwebrequest), а затем добавляет их в список, называемый finishedProxies.

По какой-то причине, когда я нажимаю кнопку запуска, вся программа зависает. У меня создалось впечатление, что Parallel создает отдельные streamи для каждого действия, оставляя stream пользовательского интерфейса отдельно, чтобы он реагировал?

private void start_Click(object sender, RoutedEventArgs e) { // Populate a list of proxies List proxies = new List(); List finishedProxies = new List(); foreach (string proxy in proxiesList.Items) { proxies.Add(proxy); } Parallel.ForEach(proxies, (i) => { string checkResult; checkResult = checkProxy(i); finishedProxies.Add(checkResult); // update ui /* status.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, new Action( delegate() { status.Content = "hello" + checkResult; } )); */ // update ui finished //Console.WriteLine("[{0}] F({1}) = {2}", Thread.CurrentThread.Name, i, CalculateFibonacciNumber(i)); }); } 

Я попытался использовать код, который был прокомментирован, чтобы внести изменения в пользовательский интерфейс внутри Parallel.Foreach, и он запустит программу после нажатия кнопки запуска. Он работал для меня раньше, но я использовал class Thread.

Как обновить пользовательский интерфейс из Parallel.Foreach и как заставить Parallel.Foreach работать так, чтобы он не заставлял пользовательский интерфейс зависеть во время работы?

Вот весь код.

Вы не должны запускать параллельную обработку в streamе пользовательского интерфейса. См. Пример в заголовке «Избегайте выполнения параллельных циклов в заголовке пользовательского интерфейса» на этой странице .

Обновление. Или вы можете просто создать новый манифест streamа и начать обработку внутри этого, как я вижу, вы сделали. В этом нет ничего плохого.

Кроме того, как указывает Джим Мишель, вы одновременно получаете доступ к спискам из нескольких streamов, поэтому там есть условия гонки. Либо замените ConcurrentBag для List , либо оберните списки внутри оператора lock каждый раз, когда вы обращаетесь к ним.

Хороший способ обойти проблемы неспособности писать в stream пользовательского интерфейса при использовании операторов Parallel – использовать Factory Factory и delegates, см. Следующий код, я использую его для перебора ряда файлов в каталоге и обрабатывает их в параллельном цикле foreach, после обработки каждого файла stream пользовательского интерфейса сигнализируется и обновляется:

 var files = GetFiles(directoryToScan); tokenSource = new CancellationTokenSource(); CancellationToken ct = tokenSource.Token; Task task = Task.Factory.StartNew(delegate { // Were we already canceled? ct.ThrowIfCancellationRequested(); Parallel.ForEach(files, currentFile => { // Poll on this property if you have to do // other cleanup before throwing. if (ct.IsCancellationRequested) { // Clean up here, then... ct.ThrowIfCancellationRequested(); } ProcessFile(directoryToScan, currentFile, directoryToOutput); // Update calling thread's UI BeginInvoke((Action)(() => { WriteProgress(currentFile); })); }); }, tokenSource.Token); // Pass same token to StartNew. task.ContinueWith((t) => BeginInvoke((Action)(() => { SignalCompletion(sw); })) ); 

И методы, которые изменяют фактический пользовательский интерфейс:

 void WriteProgress(string fileName) { progressBar.Visible = true; lblResizeProgressAmount.Visible = true; lblResizeProgress.Visible = true; progressBar.Value += 1; Interlocked.Increment(ref counter); lblResizeProgressAmount.Text = counter.ToString(); ListViewItem lvi = new ListViewItem(fileName); listView1.Items.Add(lvi); listView1.FullRowSelect = true; } private void SignalCompletion(Stopwatch sw) { sw.Stop(); if (tokenSource.IsCancellationRequested) { InitializeFields(); lblFinished.Visible = true; lblFinished.Text = String.Format("Processing was cancelled after {0}", sw.Elapsed.ToString()); } else { lblFinished.Visible = true; if (counter > 0) { lblFinished.Text = String.Format("Resized {0} images in {1}", counter, sw.Elapsed.ToString()); } else { lblFinished.Text = "Nothing to resize"; } } } 

Надеюсь это поможет!

Если кто-то любопытен, я как бы понял это, но я не уверен, что это хорошее программирование или любой способ справиться с этой проблемой.

Я создал новый stream:

 Thread t = new Thread(do_checks); t.Start(); 

и уберите все параллельные вещи внутри do_checks ().

Кажется, все в порядке.

Одна проблема с вашим кодом заключается в том, что вы вызываете FinishedProxies.Add из нескольких streamов одновременно. Это вызовет проблему, потому что List не является streamобезопасным. Вам необходимо защитить его с помощью блокировки или другого примитива синхронизации или использовать параллельную коллекцию.

Является ли это причиной блокировки пользовательского интерфейса, я не знаю. Без дополнительной информации трудно сказать. Если список proxies очень длинный, и checkProxy не займет много времени, то ваши задачи будут стоять в очереди за вызовом Invoke . Это вызовет целую кучу ожидающих обновлений пользовательского интерфейса. Это заблокирует пользовательский интерфейс, потому что stream пользовательского интерфейса занят обслуживанием этих запросов в очереди.

Это то, что, как я думаю, может происходить в вашей кодовой базе.

Обычный сценарий: вы нажимаете кнопку. Не используйте цикл Parallel.Foreach. Используйте class Dispatcher и нажмите код для запуска в отдельном streamе в фоновом режиме. После обработки фонового streamа он будет вызывать основной stream пользовательского интерфейса для обновления пользовательского интерфейса. В этом случае фоновый stream (вызываемый через Dispatcher) знает о главном streamе пользовательского интерфейса, который ему требуется для обратного вызова. Или просто сказал, что основной stream пользовательского интерфейса имеет свою личность.

Использование цикла Parallel.Foreach: как только вы вызываете цикл Paralle.Foreach, среда использует stream threadpool. Нити ThreadPool выбираются случайным образом, а исполняемый код никогда не должен делать каких-либо предположений относительно идентичности выбранного streamа. В исходном коде очень возможно, что stream диспетчера, вызванный через цикл Parallel.Foreach, не может определить stream, с которым он связан. Когда вы используете явный stream, он работает отлично, потому что явный stream имеет свою собственную идентификацию, на которую может ссылаться исполняемый код.

В идеале, если ваша главная проблема заключается в том, чтобы поддерживать пользовательский интерфейс, тогда вы должны сначала использовать class Dispatcher, чтобы нажимать код в фоновом streamе, а затем использовать то, что когда-либо было логичным, чтобы ускорить общее выполнение.

если вы хотите использовать параллельный foreach в GUI-элементе управления, например, щелчок на кнопке и т. д., тогда установите параллельный foreach в Task.Factory.StartNew, как

 private void start_Click(object sender, EventArgs e) { await Task.Factory.StartNew(() => Parallel.ForEach(YourArrayList, (ArraySingleValue) => { Console.WriteLine("your background process code goes here for:"+ArraySingleValue); }) ); }//func end 

он разрешит проблему замораживания / застревания или повесить

  • Удалить границу нижней границы в панели вкладок? (И изменить выбранный цвет)
  • Возможно ли программирование графического интерфейса?
  • java - Как бы я динамически добавлял компонент swing в gui при нажатии?
  • Картина не клиента на окне аэрозона
  • Как выравнивать представления в нижней части экрана?
  • Несколько streamов пользовательского интерфейса - Winforms
  • Настройка цвета фона элемента макета Android
  • Дизайн пользовательского интерфейса Blackberry - настраиваемый пользовательский интерфейс?
  • SwingWorker, Thread.sleep () или javax.swing.timer? Мне нужно «вставить паузу»,
  • Android 4.2.1 неправильный кернинг символов (интервал)
  • Android ViewPager с нижними точками
  • Давайте будем гением компьютера.