问题
I originally tried to use the Dispatcher class BeginInvoke method to show a message box on the main UI thread in my C# Windows Forms app. When I used that method the message box did not appear. I set a breakpoint inside the body of the delegate I passed to BeginInvoke() and it was never hit. I tried using both an Action delegate and a MethodInvoker delegate. No luck in either case.
When I used the BeginInvoke method belonging to the Form object it worked fine. Why did the Dispatch version fail silently (no exceptions or error messages)? Below are the two different versions.
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
// THIS FAILED. CONTEXT: Executing on worker thread.
MethodInvoker theMethod = new MethodInvoker(delegate()
{
string msg = "Show this message on the main UI thread.";
MessageBox.Show(msg, "Message");
});
dispatcher.BeginInvoke(theMethod);
this.BeginInvoke(theMethod);
// ---------------------------------------------------
// THIS WORKED. CONTEXT: Executing on worker thread.
MethodInvoker theMethod = new MethodInvoker(delegate()
{
string msg = "Show this message on the main UI thread.";
MessageBox.Show(msg, "Message");
});
// "this" is a Form object.
this.BeginInvoke(theMethod);
回答1:
If I'm reading your comments correctly, you are calling Dispatcher.CurrentDispatcher
from a non-UI thread. This is not how this is meant to be used.
As the documentation for Dispatcher.CurrentDispatcher says:
Gets the Dispatcher for the thread currently executing and creates a new Dispatcher if one is not already associated with the thread.
To get a valid dispatcher instance, you need to call Dispatcher.CurrentDispatcher
from the UI thread.
Also, because the documentation says that it will automatically create a dispatcher if one does not exist for the current thread, this is what explains the silent failure. You are getting a dispatcher instance, but it isn't associated to the UI thread in any way, so it isn't actually dispatching anything to the UI thread.
(Removing this, because in my tests, I get null even when I shouldn't, so it doesn't prove much it seems. The rest of the information is accurate though)
The documentation also adds:
This is not the case with the FromThread
method. FromThread
will return null
if there is not a dispatcher associated with the specified thread.
So to confirm that you are indeed getting an auto-created (invalid) dispatcher, try getting the dispatcher from Dispatcher.FromThread instead. My guess is that you will get null
.
If you want to call dispatcher.BeginInvoke
to force execution of a method on the UI thread from a worker thread, you need to call Dispatcher.CurrentDispatcher
from the UI thread and save that to a variable. You can then pass that dispatcher reference variable to the worker thread, and call BeginInvoke
on that.
// capture and save dispatcher from UI thread
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
// then you can do this from your worker thread:
dispatcher.BeginInvoke(theMethod);
Alternatively, use this.BeginInvoke
like you are doing already.
Or better yet, you can try using tasks combined with the new async-await
keywords for this sort of thing.
EDIT
For completeness, I should explain why Control.BeginInvoke
does work correctly.
As the documentation for Control.BeginInvoke says:
Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on.
And later it also adds:
You can call this method from any thread.
The point is that, when you call Control.BeginInvoke
, it doesn't use the current thread to determine how to execute the delegate. It remembers which thread the control was created on (the UI thread), and makes sure to execute the delegate on that thread.
So, as long as your control is created on the UI thread (as it should), then BeginInvoke
works from any thread. This is actually quite similar to the Dispatcher
in that, as long as you obtain the Dispatcher
instance from the UI thread first, then you can call Dispatcher.BeginInvoke
from any thread as well.
来源:https://stackoverflow.com/questions/31713263/why-did-dispatcher-begininvoke-fail-where-control-begininvoke-succeed-in-c-sharp