为什么在C#Windows Forms应用程序中Control BeginInvoke成功的地方调度程序BeginInvoke失败? [英] Why did dispatcher BeginInvoke fail where Control BeginInvoke succeed in C# Windows Forms app?

查看:140
本文介绍了为什么在C#Windows Forms应用程序中Control BeginInvoke成功的地方调度程序BeginInvoke失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最初尝试使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆