BackgroundWorker.ReportProgress()和Control.BeginInvoke()之间的区别

时间:2010-05-19 13:39:04

标签: winforms multithreading controls backgroundworker invoke

以下选项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()这样的本机函数,无法准确地找出反射器的流量(讨厌的编译器goto优化)

而BackgroundWorker.Invoke()似乎使用:

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

这似乎最终调用了ThreadPool.QueueUserWorkItem()

(我只是猜测这些是每个案例的相关函数调用。)如果我理解正确,使用ThreadPool不会保证执行顺序,而使用Post机制会。也许这可能是一个潜在的差异? (编辑 - 我无法综合这样的情况 - 在这两种情况下似乎都保留了调用顺序,至少在我的简单测试中是这样。)

谢谢!

3 个答案:

答案 0 :(得分:2)

一个很大的区别是Control.Invoke将阻塞,直到UpdateGUI调用已执行并完成,而BackgroundWorker.ReportProgress阻止(它将立即返回,之前,BackgroundWorker会引发事件。)

如果您希望它们的行为相同,请调用Control.BeginInvoke(不会阻止)。

答案 1 :(得分:2)

他们都是一样的。您在BackgroundWorker中看到的来电使用SynchronizationContext。实际上Post()方法的默认实现使用线程池,但在启动Windows窗体应用程序时,默认同步上下文被WindowsFormsSynchronizationContext替换,实际调用Control.BeginInvoke()

答案 2 :(得分:0)

我发现了一个显着的差异。在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");
    }
}