以下选项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机制会。也许这可能是一个潜在的差异? (编辑 - 我无法综合这样的情况 - 在这两种情况下似乎都保留了调用顺序,至少在我的简单测试中是这样。)
谢谢!
答案 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");
}
}