Control Invoke详解(从不是创建控件的线程调用)
2010-06-12 18:48
218 查看
4/10/2008
在跨线程更新的时候,Control会检查CurrentThread是否为创建Control的线程,并报错
所以,Control.InvokeRequired返回的实际上只是:CurrentThread != ControlCreatThread而已,在创建者线程和其它线程中,该值不同
==========================
在设计中为了让界面与逻辑分离,我的做法是使用事件,界面只要响应事件来处理界面的显示就行了。而事件在逻辑处理中可能由不同的线程引发,这些事件的响应方法在修改界面中的控件内容时便会引发一个异常。
这时就用到了Control.InvokeRequired 属性 与Invoke方法。
MSDN中说:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
下面来说下这个的用法(我的一般做法):
首先定义一个委托,与这个事件处理函数的签名一样委托,当然直接使用该事件的委托也是可以的,如:
private delegate void InvokeCallback(string msg);
然后就是判断这个属性的值来决定是否要调用Invoke函数:
void m_comm_MessageEvent(string msg)
{
if(txtMessage.InvokeRequired)
{
InvokeCallbackmsgCallback = new InvokeCallback(m_comm_MessageEvent);
txtMessage.Invoke(msgCallback, new object[] { msg });
}
else
{
txtMessage.Text = msg;
}
}
说明:这个函数就是事件处理函数,txtMessage是一个文本框。
这样就做到了窗体中控件的线程安全性。
============================
Control.Invoke方法
既:Control不能在创建它的thread之外被调用。通过invoke来保证Control线程安全。在跨线程更新的时候,Control会检查CurrentThread是否为创建Control的线程,并报错
所以,Control.InvokeRequired返回的实际上只是:CurrentThread != ControlCreatThread而已,在创建者线程和其它线程中,该值不同
==========================
在设计中为了让界面与逻辑分离,我的做法是使用事件,界面只要响应事件来处理界面的显示就行了。而事件在逻辑处理中可能由不同的线程引发,这些事件的响应方法在修改界面中的控件内容时便会引发一个异常。
这时就用到了Control.InvokeRequired 属性 与Invoke方法。
MSDN中说:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
下面来说下这个的用法(我的一般做法):
首先定义一个委托,与这个事件处理函数的签名一样委托,当然直接使用该事件的委托也是可以的,如:
private delegate void InvokeCallback(string msg);
然后就是判断这个属性的值来决定是否要调用Invoke函数:
void m_comm_MessageEvent(string msg)
{
if(txtMessage.InvokeRequired)
{
InvokeCallbackmsgCallback = new InvokeCallback(m_comm_MessageEvent);
txtMessage.Invoke(msgCallback, new object[] { msg });
}
else
{
txtMessage.Text = msg;
}
}
说明:这个函数就是事件处理函数,txtMessage是一个文本框。
这样就做到了窗体中控件的线程安全性。
============================
[C#学习]在多线程中如何调用Winform | |||
问题的产生: 我的WinForm程序中有一个用于更新主窗口的工作线程(worker thread),但文档中却提示我不能在多线程中调用这个form(为什么?),而事实上我在调用时程序常常会崩掉。请问如何从多线程中调用form中的方法呢? 解答: 每一个从Control类中派生出来的WinForm类(包括Control类)都是依靠底层Windows消息和一个消息泵循环(message pump loop)来执行的。消息循环都必须有一个相对应的线程,因为发送到一个window的消息实际上只会被发送到创建该window的线程中去。其结果是,即使提供了同步(synchronization),你也无法从多线程中调用这些处理消息的方法。大多数plumbing是掩藏起来的,因为WinForm是用代理(delegate)将消息绑定到事件处理方法中的。WinForm将Windows消息转换为一个基于代理的事件,但你还是必须注意,由于最初消息循环的缘故,只有创建该form的线程才能调用其事件处理方法。如果你在你自己的线程中调用这些方法,则它们会在该线程中处理事件,而不是在指定的线程中进行处理。你可以从任何线程中调用任何不属于消息处理的方法。 Control类(及其派生类)实现了一个定义在System.ComponentModel命名空间下的接口 -- ISynchronizeInvoke,并以此来处理多线程中调用消息处理方法的问题:
调用者还可以检查InvokeRequired属性,因为你既可以在同一线程中调用ISynchronizeInvoke也可以将它重新定位(redirect)到其他线程中去。如果InvokeRequired的返回值是false的话,则调用者可以直接调用该对象的方法。 比如,假设你想要从另一个线程中调用某个form中的Close方法,那么你可以使用预先定义好的的MethodInvoker代理,并调用Invoke方法:
C# 在正确的线程中写入调用 列表A. Calculator类的Add()方法用于将两个数字相加。如果用户直接调用Add()方法,它会在该用户的线程中执行调用,而用户可以通过ISynchronizeInvoke.Invoke()将调用写入正确的线程中。 列表A:
还值得一提的是ISynchronizeInvoke方法并非安全类型。 类型不符会导致在执行时被抛出异常,而不是编译错误。所以在使用ISynchronizeInvoke时要格外注意,因为编辑器无法检查出执行错误。 实现ISynchronizeInvoke要求你使用一个代理来在后期绑定(late binding)中动态地调用方法。每一种代理类型均提供DynamicInvoke()方法: public object DynamicInvoke(object[] args); 理论上来说,你必须将一个方法代理放到一个需要提供对象运行的真实的线程中去,并使Invoke() 和BeginInvoke()方法中的代理中调用DynamicInvoke()方法。ISynchronizeInvoke的实现是一个非同一般的编程技巧,本文附带的源文件中包含了一个名为Synchronizer的帮助类(helper class)和一个测试程序,这个测试程序是用来论证列表A中的Calculator类是如何用Synchronizer类来实现ISynchronizeInvoke的。Synchronizer是ISynchronizeInvoke的一个普通实现,你可以使用它的派生类或者将其本身作为一个对象来使用,并将ISynchronizeInvoke实现指派给它。 用来实现Synchronizer的一个重要元素是使用一个名为WorkerThread的嵌套类(nested class)。WorkerThread中有一个工作项目(work item)查询。WorkItem类中包含方法代理和参数。Invoke()和BeginInvoke()用来将一个工作项目实例加入到查询里。WorkerThread新建一个.NET worker线程,它负责监测工作项目的查询任务。查询到项目之后,worker会读取它们,然后调用DynamicInvoke()方法。 |
相关文章推荐
- C# 解决 调用线程非安全的控件xx不是在本线程上创建 异常
- 调用不是本线程创建的windows控件会报错
- UI多线程调用:线程间操作无效: 从不是创建控件"Form1"的线程访问它.
- 线程间操作无效: 从不是创建控件(对 Windows 窗体控件进行线程安全调用)
- 线程间操作无效: 从不是创建控件的线程访问它
- 线程间操作无效: 从不是创建控件“listBox1”的线程访问它
- 线程间操作无效: 从不是创建控件“t1”的线程访问它
- C#线程之间操作无效|不允许访问控件(c# 线程间操作无效: 从不是创建控件“”的线程访问它)
- 线程间操作无效: 从不是创建控件“textBox1”的线程访问它。
- 小结“线程间操作无效: 从不是创建控件的线程访问它” 错误的解决方法
- 线程间操作无效:从不是创建控件“XX”的线程访问它
- 解决 线程间操作无效: 从不是创建控件的线程访问它
- 线程间操作无效: 从不是创建控件“...”的线程访问它
- 线程间操作无效: 从不是创建控件“lb_RealError”的线程访问它。
- 线程间操作无效,从不是创建控件的线程访问控件的解决方案
- 线程间操作无效: 从不是创建控件“button1”的线程访问它。
- 线程间操作无效: 从不是创建控件的线程访问它
- 线程间操作无效:从不是创建控件”txtContent”的线程访问它
- 线程间操作无效: 从不是创建控件的线程访问它
- 线程间操作无效: 从不是创建控件“...”的线程访问它。