我正在努力了解如何正确使用BeginInvoke。我在一个控制台应用程序中编写了一个小测试,其中我要做的就是使用BeginInvoke调用一个函数来制作一个标题弹出的100x100窗口。我悲惨地失败了。这就是我所拥有的,我知道这可能只是对Threads的理解不够(不是我强大的西装),但是我被卡住了,没有窗口弹出我只是在主要等待按键的读取线上结束。执行从ThreadUITest开始。
static void ThreadUITest()
{
ThreadStart starter = new ThreadStart(threadFunc1);
Thread test = new Thread(starter);
test.IsBackground = true;
test.SetApartmentState(ApartmentState.STA);
test.Start();
}
static void threadFunc1()
{
dispatcher = Dispatcher.CurrentDispatcher; //Statically declared earlier
ThreadStart starter = new ThreadStart(threadFunc2);
Thread test = new Thread(starter);
test.IsBackground = true;
test.Start();
}
static void threadFunc2()
{
Action method = Draw;
Console.WriteLine("I'm here!");
//dispatcher.BeginInvoke( (Action)(() => {Draw();}),DispatcherPriority.Render, null);
dispatcher.BeginInvoke(method, DispatcherPriority.Send, null);
}
static void Draw()
{
Window win = new Window();
win.Height = 100;
win.Width = 100;
win.Title = "A Window!";
win.Show();
}
感谢您的帮助。
答案 0 :(得分:2)
您需要在threadFunc1
// statically declared earlier, although you don't need to keep a reference to it as
// WPF will keep it in Application.Current
application = new Application();
application.Run(); // thread1 is now our "UI" thread
为什么要解决这个问题?
Dispatcher
对象提供了一个界面,用于让线程为您做一些工作(通过BeginInvoke
或Invoke
)。
为了让一个线程能够处理任何“do work”消息,它必须运行某种事件循环,它位于并等待下一条消息要处理 - 如果它没有这样做,那么它就不会不能处理任何东西,它会被卡住。
从thread1调用Dispatcher.CurrentDispatcher
将在该线程上创建一个新的调度程序(如果还没有那个[1]) - 它为我们提供了向线程发送消息的接口。
dispatcher.BeginInvoke
所做的是为该线程的消息队列添加一个条目,但该线程尚未运行任何消息循环。我们可以将消息排队到它,但它不会接收它们并运行它们 - 这就是没有任何反应的原因。
因此,我们需要让该线程开始运行消息循环
Application.Run()
方法是WPF框架方法,它正是这样做的。 Application.Run
方法永远不会返回(无论如何都要调用Application.Shutdown
),它会启动一个消息循环,以便此后开始处理消息。我觉得把它“接管”线程是有用的。
现在有了这个改变,当thread2func调用dispatcher.BeginInvoke
时,Application.Run
内的消息循环代码变为“哦,看一条消息,我会处理它” - 它得到BeginInvoke
方法,并做了它告诉的事情(在这种情况下,执行你的Draw
功能),一切都很好
注意:根据Ark-kun's answer您也可以调用Dispatcher.Run
在该线程上启动消息循环而不创建Application对象(Application.Run在内部执行此操作)。通常我觉得创建一个应用程序对象更好,因为它更“正常”,稍后您可能会写的其他一些代码可能期望Application对象存在
[1]仅供参考,这就是为什么调用Dispatcher.CurrentDispatcher
是危险的,你应该避免它。如果从现有UI线程中调用Dispatcher.CurrentDispatcher
,则会返回对正确调度程序的引用
如果你不小心从另一个线程中调用它,从逻辑上讲,你会认为它将返回对现有调度程序的引用。但是没有 - 相反,它会创建一个第二个调度程序,指向我们的其他线程 - 但是我们的其他线程将不会运行消息循环,我们将再次卡住。我建议除了第一次以外从不打电话给Dispatcher.CurrentDispatcher
。
一旦您的应用程序启动并运行,您通常无需执行此操作,因为所有WPF对象(Window,Button等)都具有Dispatcher
属性,您可以使用该属性来获取正确的无论如何,调度员
答案 1 :(得分:1)
尝试在Dispatcher.Run()
的末尾调用threadFunc1
。