我一直看到/使用代码的某种形式或方式:
public void method1(Object sender, EventArgs args)
{
if(dataGridView1.InvokeRequired)
dataGridView1.Invoke(new EventHandler(method1), null);
else
// Do something to dataGridView1
}
我的问题是......当我使用Invoke
时,GUI线程会发生什么?它是否像一个中断,线程将立即执行method1
?
答案 0 :(得分:4)
是否像中断...
不,一点也不。在繁忙执行代码时,没有安全的方法来中断线程。这导致了一种特别令人讨厌的问题,称为“重入错误”。这是固件程序员在嵌入式系统上实现中断处理程序时所遇到的一种错误。关于this web page中的一些背景知识。
程序的UI线程以不同的方式解决了这个问题,它在Producer-consumer problem的标准解决方案中扮演了消费者的角色。成分是生产者将消息发布到消费者线程中的调度程序循环的线程安全队列。它从队列中检索消息并执行与消息关联的消息处理程序。这通常被描述为“抽取消息循环”。生产者通常是操作系统,为用户按键或移动鼠标等事件生成消息。但它可以是生成消息的任何代码,包括另一个线程。
Winforms为此方案(调用队列)添加了一个额外的队列。其中存储了代码创建的委托对象以及您提供的参数。 Begin / Invoke为调用队列添加一个条目,并对PostMessage()进行pinvoke,让UI线程知道需要完成某些事情。
如果UI线程忙于执行代码,比如说处理一个绘制事件,那么它就不知道了。在它再次空闲,重新进入调度程序循环并调用GetMessage()之前,它不会注意到已发布的消息。或者它已经空闲,然后它会很快响应消息。它检索调用队列中的条目并执行委托目标。
在Invoke而不是BeginInvoke的情况下,它将在队列条目中调用ManualResetEvent的Set()方法。您的线程正在等待它,然后它将继续执行。如果委托方法失败,那么此时引发的异常也将在线程中重新引发。
您可以从其工作方式中得出一些基本结论:
答案 1 :(得分:2)
简单的答案是:在当前线程(GUI线程)中调用method1。它非常相似:
public void method1(Object sender, EventArgs args)
{
if(dataGridView1.InvokeRequired)
method1();
else
// Do something to dataGridView1
}
除了它还执行已在marshaller控件中排队的所有先前方法。
这里有一些细节反编译Control.Invoke
。
正如在MSDN上所解释的那样,Invoke
“搜索控件的父链,直到找到具有窗口句柄的控件或表单”。我们称之为“父”控件:marshaler
。
然后,Invoke
调用marshaler.MarshaledInvoke
并将委托作为参数执行。
在MarshaledInvoke
中,执行的第一个操作之一是检查当前线程(调用Invoke
的线程是否与附加到{{{{1}的窗口句柄的线程相同) 1}}。它将结果存储到变量marshaler
。
将新任务排入与syncSameThread
关联的队列中。
然后,如果marshaler
为syncSameThread
,则调用true
,在当前线程中执行当前控件的任务队列中的所有任务(此处为InvokeMarshaledCallbacks
})。