问题
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