一、 引言
在C#中,经常用到这样一个场景,Windows Form程序启动一个工作者线程执行一部分工作,这样做是为了避免速度慢的工作如果直接调用会使得主Form停止响应一段时间。既然启动了线程,就避免不了线程之间数据传递的事情,相信你有很多种办法能解决,总之注意同步和互斥操作就好。我想说的是,工作线程处理中可能想操作某个主线程的Windows Form的Control,比如按钮,ListView等等更新工作状态之类,直接控制是不行的,不能够跨线程操作另一个线程创建的Windows Form控件。
二、相关知识介绍
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。因此,如果从另一个线程调用控件的方法,那么必须使用控件的 Invoke 方法来将调用封送到适当的线程。
属性Control.InvokeRequired 获取一个值,该值指示调用方在调用控件的方法时是否必须通过控件的Invoke方法来实现。该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
当从另一个线程调用控件的方法或属性时,只有属性InvokeRequired 和方法Invoke、BeginInvoke、EndInvoke CreateGraphics可以直接调用,控件的其他属性和方法的调用必须通过调用控件的Invoke方法来实现。
控件Invoke方法的两种重载形式
(1)public Object Invoke (Delegate method)
在拥有此控件的基础窗口句柄的线程上执行指定的委托
返回值为正在被调用的委托的返回值,或者如果委托没有返回值,则为 空引用
(2)public Object Invoke (Delegate method, params Object[] args)
在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托。
method 一个方法委托,它采用的参数的数量和类型与 args 参数中所包含的相同。
args 作为指定方法的参数传递的对象数组。如果此方法没有参数,该参数可以是 空引用
返回值为正被调用的委托返回值;如果该委托没有返回值,则为 空引用
三、总结
假设在主线程main_thread中有一个控件C,现在为了工作的需要又开启了一个线程thread1。在线程thread1中需要对主线程main_thread中的控件C进行一些操作(例如修改该控件的属性,调用该控件的方法),为了线程安全性考虑,在线程thread1中不能直接操作主线程main_thread中的控件C。具体方法如下:
(1) 先调用控件C的属性InvokeRequired,判断在线程thread1中是否能直接操作主线程main_thread中的控件C。
(2) 如果不能,就必须使用控件C的Invoke方法来将对控件C的一些操作封送到适当的线程。