代码之家  ›  专栏  ›  技术社区  ›  Ohad Schneider

BackgroundWorker.ReportProgress()和Control.BeginInvoke()之间的差异

  •  3
  • Ohad Schneider  · 技术社区  · 16 年前

    以下选项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);
        }
    

    查看Reflector时,control.beginInvoke()似乎使用了:

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

    它似乎最终调用了一些本机函数,比如postmessage(),无法准确地从Reflector中找出流程(烦人的编译器goto优化)

    而backgroundWorker.invoke()似乎使用:

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

    似乎最终会调用threadpool.queueUserWorkItem()。

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

    谢谢!

    3 回复  |  直到 15 年前
        1
  •  2
  •   Eli Arbel    16 年前

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

        2
  •  2
  •   MusiGenesis    16 年前

    一个很大的区别是 Control.Invoke 将一直阻止,直到执行并完成updateGUI调用,而 BackgroundWorker.ReportProgress 阻止(它将立即返回, 之前 后台工作人员引发事件)。

    如果你想让他们表现得一样,打电话给 Control.BeginInvoke (这不会阻挡)相反。

        3
  •  0
  •   Ohad Schneider    15 年前

    我发现了一个显著的区别。在bgw运行时关闭窗体将导致此.invoke()和此.beginInvoke()引发ObjectDisposedException。bgw报告进度机制绕过了这一点。为了享受两个世界的最佳状态,下面的模式工作得很好

    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");
        }
    }
    
    推荐文章