Проблема перекрестного чтения с помощью BackgroundWorker и обновления строки состояния - PullRequest
0 голосов
/ 08 марта 2012

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

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);

или мне нужно как-то изменить его?

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

Редактировать 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 или должны использоваться вызовы в новом событии?

1 Ответ

4 голосов
/ 08 марта 2012

Документация для 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

РЕДАКТИРОВАТЬ:

Вызов 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
}

РЕДАКТИРОВАТЬ 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;
}
...