Разница между BackgroundWorker.ReportProgress () и Control.BeginInvoke () - PullRequest
3 голосов
/ 19 мая 2010

В чем разница между вариантами 1 и 2 в следующем?

    private void BGW_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i=1; i<=100; i++)
        {
            string txt = i.ToString();
            if (Test_Check.Checked)
                //OPTION 1
                Test_BackgroundWorker.ReportProgress(i, txt); 
            else
                //OPTION 2
                this.BeginInvoke((Action<int, string>)UpdateGUI, 
                                  new object[] {i, txt});
        }
    }

    private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        UpdateGUI(e.ProgressPercentage, (string)e.UserState);
    }

    private void UpdateGUI(int percent, string txt)
    {
        Test_ProgressBar.Value = percent;
        Test_RichTextBox.AppendText(txt + Environment.NewLine);
    }

Глядя на отражатель, Control.BeginInvoke (), по-видимому, использует:

this.FindMarshalingControl().MarshaledInvoke(this, method, args, 1);

Который, кажется, в конечном итоге вызывает некоторые нативные функции, такие как PostMessage (), не может точно определить поток от рефлектора (надоедливая оптимизация компилятора)

Принимая во внимание, что BackgroundWorker.Invoke () используется:

this.asyncOperation.Post(this.progressReporter, args);

Который, кажется, в конечном итоге вызывает ThreadPool.QueueUserWorkItem ()

(Я просто предполагаю, что это соответствующие вызовы функций для каждого случая.) Если я правильно понимаю, использование ThreadPool не гарантирует порядок выполнения, тогда как использование механизма Post будет. Возможно, это будет потенциальная разница? ( РЕДАКТИРОВАТЬ - я не мог синтезировать такую ​​ситуацию - порядок вызовов, кажется, сохраняется в обоих случаях, по крайней мере, в моих простых тестах.)

Спасибо!

Ответы [ 3 ]

2 голосов
/ 20 мая 2010

Они оба одинаковы. Вызов, который вы видите в BackgroundWorker, использует SynchronizationContext. Действительно, реализация по умолчанию метода Post() использует пул потоков, но при запуске приложения Windows Forms контекст синхронизации по умолчанию заменяется на WindowsFormsSynchronizationContext, который фактически вызывает Control.BeginInvoke().

2 голосов
/ 19 мая 2010

Одно большое отличие состоит в том, что Control.Invoke будет блокироваться до тех пор, пока не будет выполнен и завершен вызов UpdateGUI, тогда как BackgroundWorker.ReportProgress будет не блокировать (он вернется немедленно, до BackgroundWorker вызывает событие).

Если вы хотите, чтобы они вели себя одинаково, вместо этого вызовите Control.BeginInvoke (который не блокирует).

0 голосов
/ 04 июля 2010

Я обнаружил значительную разницу. Закрытие формы во время работы BGW приведет к тому, что this.Invoke () и this.BeginInvoke () вызовут исключение ObjectDisposedException. Механизм BGW ReportProgress обходит это. Чтобы насладиться лучшим из обоих миров, хорошо работает следующий шаблон

public partial class MyForm : Form
{
    private void InvokeViaBgw(Action action)
    {
        Packing_Worker.ReportProgress(0, action);
    }

    private void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (this.IsDisposed) return; //You are on the UI thread now, so no race condition

        var action = (Action)e.UserState;
        action();
    }

    private private void BGW_DoWork(object sender, DoWorkEventArgs e)
    {
       //Sample usage:
       this.InvokeViaBgw(() => MyTextBox.Text = "Foo");
    }
}
...