为什么在C#Windows Forms应用程序中Control BeginInvoke成功的地方调度程序BeginInvoke失败? [英] Why did dispatcher BeginInvoke fail where Control BeginInvoke succeed in C# Windows Forms app?
问题描述
我最初尝试使用Dispatcher类BeginInvoke方法在C#Windows Forms应用程序的主UI线程上显示消息框。当我使用该方法时,消息框没有出现。我在传递给BeginInvoke()的委托的主体内设置了一个断点,它没有被击中。我尝试同时使用 Action 委托和 MethodInvoker 委托。
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.
当我使用属于Form对象的BeginInvoke方法时,工作正常。为什么分派版本无提示地失败(没有异常或错误消息)?下面是两个不同的版本。
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);
推荐答案
如果我正确阅读了您的评论,那么您从非UI线程调用 Dispatcher.CurrentDispatcher
。
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.
作为 Dispatcher.CurrentDispatcher 说:
获取如果调度程序 尚未与该线程 关联,则为当前正在执行的线程 创建一个新的Dispatcher。
Gets the Dispatcher for the thread currently executing and creates a new Dispatcher if one is not already associated with the thread.
要获取有效的调度程序实例,您需要调用 Dispatcher.CurrentDispatcher
To get a valid dispatcher instance, you need to call Dispatcher.CurrentDispatcher
from the UI thread.
此外,因为文档说如果当前线程不存在该调度程序,它将自动创建一个调度程序,这就是无声失败的原因。 。您正在获得一个调度程序实例,但是它没有以任何方式与UI线程相关联,因此它实际上并没有向UI线程调度任何东西。
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.
(删除它,因为在我的测试中,即使我不应该也得到null,所以看起来似乎没有什么证明。其余信息是准确的)
文档还添加了:
(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:
FromThread
方法不是这种情况。如果没有与指定线程关联的调度程序,则 FromThread
将返回 null
。
This is not the case with the FromThread
method. FromThread
will return null
if there is not a dispatcher associated with the specified thread.
因此,要确认您确实正在获取自动创建的(无效)调度程序,请尝试从 Dispatcher.FromThread 代替。我的猜测是,您将得到 null
。
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
.
如果您要致电 dispatcher.BeginInvoke
强制从工作线程在UI线程上执行方法,您需要从UI调用 Dispatcher.CurrentDispatcher
线程并将其保存到变量。然后,您可以将该调度程序引用变量传递给工作线程,并在该线程上调用 BeginInvoke
。
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);
或者,使用 this.BeginInvoke
或者更好的是,您可以尝试结合使用新的 async-await
关键字的任务
Or better yet, you can try using tasks combined with the new async-await
keywords for this sort of thing.
编辑
为了完整起见,我应该解释一下为什么 Control.BeginInvoke
确实可以正常工作。
For completeness, I should explain why Control.BeginInvoke
does work correctly.
作为 Control.BeginInvoke 说:
在创建控件的基础句柄的线程上异步执行指定的委托。
Executes the specified delegate asynchronously on the thread that the control's underlying handle was created on.
它还会添加:
您可以从任何线程调用此方法。
You can call this method from any thread.
关键是,当您调用 Control.BeginInvoke
时,它不使用当前线程来检测深入了解如何执行委托。它会记住控件是在哪个线程(UI线程)上创建的,并确保在该线程上执行委托。
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.
因此,只要创建了控件在UI线程上(应该如此),然后 BeginInvoke
可以在任何线程上工作。实际上,这与 Dispatcher
十分相似,只要您首先从UI线程获取 Dispatcher
实例即可。 ,那么您也可以从任何线程调用 Dispatcher.BeginInvoke
。
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.
这篇关于为什么在C#Windows Forms应用程序中Control BeginInvoke成功的地方调度程序BeginInvoke失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!