Diffrernce between BackgroundWorker.ReportProgress() and Control.BeginInvoke()

∥☆過路亽.° 提交于 2019-12-23 21:13:04

问题


What is the difference between options 1 and 2 in the following?

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

Looking at reflector, the Control.BeginInvoke() appears to use:

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

Which seems to eventually call some native functions like PostMessage(), couldn't exactly figure out the flow from reflector (pesky compiler goto optimizations)

Whereas BackgroundWorker.Invoke() appears to use:

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

Which seems to eventually call ThreadPool.QueueUserWorkItem()

(I'm just guessing these are the relevant function calls for each case.) If I understand correctly, using the ThreadPool would not guarantee execution order whereas using the Post mechanism would. Perhaps that would be a potential difference ? (EDIT - I couldn't synthesize such a situation - call order seems to be preserved in both cases, at least in my simple tests.)

Thanks!


回答1:


They are both the same. The call you're seeing in BackgroundWorker uses SynchronizationContext. Indeed the default implementation of the Post() method uses the thread pool, but when starting a Windows Forms app, the default synchronization context is replaced by WindowsFormsSynchronizationContext, which actually calls Control.BeginInvoke().




回答2:


One big difference is that Control.Invoke will block until the UpdateGUI call has been executed and completed, whereas BackgroundWorker.ReportProgress will not block (it will return immediately, before the BackgroundWorker raises the event).

If you want them to behave the same, call Control.BeginInvoke (which doesn't block) instead.




回答3:


I've found a significant difference. Closing the form while the BGW is running will cause this.Invoke() and this.BeginInvoke() to throw an ObjectDisposedException. The BGW ReportProgress mechanism circumvents that. To enjoy the best of both worlds, the following pattern works nicely

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


来源:https://stackoverflow.com/questions/2865971/diffrernce-between-backgroundworker-reportprogress-and-control-begininvoke

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!