2012-03-08 3 views
0

Я работаю над инструментом, который использует BackgroundWorker для выполнения операции ping с регулярным интервалом. Я сталкиваюсь с проблемой с событием BackgroundWorker ProgressChanged. Код для ProgressChanged Event ниже:Проблема с CrossThreading с обновлением BackgroundWorker и statusstrip

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      ProgressUpdated update = (ProgressUpdated)e.UserState; 
      if (sender.ToString() == "System.ComponentModel.BackgroundWorker") 
      { 
       toolStripStatusLabel1.Text = update.GeneralStatus; 
       toolStripProgressBar1.Value = update.ProgressStatus; 
       toolStripStatusLabel2.Text = update.SpecificStatus; 
      } 
      else 
      { 
       toolStripStatusLabel1.Text = update.GeneralStatus; 
       toolStripProgressBar2.Value = update.ProgressStatus; 
       toolStripStatusLabel3.Text = update.SpecificStatus; 
      } 
     } 

событие ProgressChanged вызывается как в BackgroundWork, где он обновляет первые значения и от pingcompletedcallback случае, когда пинг заканчивается. Я только сталкиваюсь с проблемой перекрестной резьбы, когда событие ProgressChanged запускается из события PingCompletedCallback. Он выдает ошибку, когда идет обновление второй строки выполнения.

Я не могу понять, почему это происходит для одного из вызовов, но не другого.

Является ли PingCompletedCallBack происходящим в потоке BackgroundWorker, и почему он вызывает проблемы с перекрестными потоками?

Если да, то как мне поднять событие, чтобы оно обрабатывалось в потоке пользовательского интерфейса, а не на рабочем столе?

Edit:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     BackgroundWorker worker = sender as BackgroundWorker; 
     // creates ping and sends it async 
     ProgressUpdated args = new ProgressUpdated(string1, int1, string 2); 
     worker.ReportProgress(0,args); 
     // rest of thread for cleanup when cancellation is called 
    } 
private void PingCompletedCallback(object sender, PingCompletedEventArgs e) 
    { 
      // handle the ping response 
      ProgressUpdated update = new ProgressUpdated(string1, int1, string2); 
      ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update); 
      backgroundWorker1_ProgressChanged(this, changed); 
      // handle other types of responses 

    } 

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

Поскольку мое понимание было неправильным, будет ли у PingCompletedCallBack доступ к методу ReportProgress для фонового рабочего?

я мог бы изменить в PingCompletedCallback:

ProgressChangedEventArgs changed = new ProgressChangedEventArgs(1,update); 
backgroundWorker1_ProgressChanged(this, changed); 

к:

backgroundWorker1.ReportProgress(1, update); 

или мне нужно, чтобы изменить его каким-то другим способом?

Спасибо за помощь.

Edit 2:

Изменено событие ProgrssChanged

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
     { 
      ProgressUpdated update = (ProgressUpdated)e.UserState; 
      toolStripStatusLabel1.Text = update.GeneralStatus; 
      toolStripProgressBar1.Value = update.ProgressStatus; 
      toolStripStatusLabel2.Text = update.SpecificStatus; 
     } 

Затем я создал второе событие обновления

private void PingUpdate (object sender, ProgressUpdated e) 
    { 
     toolStripStatusLabel1.Text = e.GeneralStatus; 
     toolStripProgressBar2.Value = e.ProgressStatus; 
     toolStripStatusLable3.Text = e.SepcificStatus; 
    } 

Единственное, что у меня осталось это назвать новое событие из PingCompletedCallback в таким образом, как он выполняется в потоке пользовательского интерфейса. Является ли это, когда будет использоваться оператор Invoke или должны использоваться Invokes в новом событии?

+0

Примечание стороны: лучше сказать "отправитель BackgroundWorker", чем sender.ToString(). Поскольку ProgressChanged выполняется в потоке пользовательского интерфейса, он не требует Invoke. –

+0

Вам не нужно использовать какой-либо вызов в методе backgroundWorker1_ProgressChanged. Используйте ReportProgress для отчета о прогрессе, так как это именно цель этих фоновых работников; синхронизировать фоновые задачи с графическим интерфейсом. Не вызывайте backgroundWorker1_ProgressChanged (это, изменено), поскольку он будет вызван из рабочего потока. – Philippe

ответ

4

В документации для BackgroundWorker указано, что вы не должны манипулировать объектами пользовательского интерфейса с помощью метода DoWork и что любые изменения в объектах пользовательского интерфейса должны выполняться через ReportProgress. Я не смотрел на отражатель, но он, вероятно, выполняет скрытый «Invoke» для вас. Независимо от того, что поднимает ваше событие PingCompleted, вероятно, выполняется внутри рабочего потока или какого-то другого потока, который не основной поток.

В окне нитей отладчика Visual Studio вы увидите, что DoTask не выполняется в основном потоке; однако, когда вызывается ReportProgress, обработчик выполняется на основном потоке. Поскольку ваши элементы управления, вероятно, были созданы в основном потоке, вы не видите исключения. enter image description here

Теперь, если вы пытаетесь вызвать backgroundWorker1_ProgressChanged явно в рамках метода DoWork, то backgroundWorker1_ProgressedChanged будет выполняться в том же потоке, что выполняет метод DoWork, или, в вашем случае, метод, который повышающий PingCompleted событие: enter image description here

Вы, вероятно, можете решить это перекрестное исключение потоков путем добавления InvokeRequired проверки в пределах вашего backgroundWorker1_ProgressChanged обработчика, или обработчик маршрута PingCompleted позвонить ReportProgress

EDIT:

Вызов ReportProgress от обработчика PingCompleted не будет работать, потому что вы потеряете оригинального отправителя.

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged), sender, e); 
     return; 
    } 

    // The rest of your code goes here 
} 

EDIT 2 Ответ:

private void PingUpdate (object sender, ProgressUpdated e) 
{ 
    if (InvokeRequired) 
    { 
     Invoke(new Action<object, ProgressUpdated>(PingUpdate), sender, e); 
     return; 
    } 

    toolStripStatusLabel1.Text = e.GeneralStatus; 
    toolStripProgressBar2.Value = e.ProgressStatus; 
    toolStripStatusLable3.Text = e.SepcificStatus; 
} 
+0

спасибо за визуальные эффекты, я не знал, что я мог бы смотреть на потоки в такой детали. будет ли у PingCompletedCallBack доступ к фоновому рабочему столу, чтобы сделать вызов ReportProgress? –

+0

Я только заметил, что ваш обработчик имеет два разных индикатора выполнения, и вы используете отправителя, чтобы определить, какой индикатор прогресса обновляется. Если вы идете с использованием подхода маршрутизации, то ваш «отправитель» всегда будет фоновым работником. У вас есть как минимум два варианта. Создайте отдельный обработчик для завершенного события ping и обновите компоненты интерфейса (с помощью Invoke). Это избавит вас от неприятного, если (отправитель ... проверяет). Если вы непреклонны в использовании одного обработчика, тогда вам нужно будет добавить Invoke-проверки в обработчик хода – Tung

+0

. Я думаю, что я буду отделять вызовы обновления, чтобы удалить неудобный if (sender is ...) check. Лучше ли выполнять Invoke в событии обновления или при вызове события? См. Edit 2 –