如何解决DotNET中跨线程访问控件的问题
朱二(2008.10,转载请保留作者)
如果您对这篇小文感兴趣的话,或许您在编程中时候碰到过以下类似的异常提示:
1、线程间操作无效: 从不是创建控件“X”的线程访问它。(X是控件名称)
2、"在该控件上执行的操作正从错误的线程调用。使用 Control.Invoke 或 Control.BeginInvoke 封送到正确的线程才能执行此操
作。"
看到这种提示,极有可能是跨线程访问控件造成的。举个小例子,窗体FormA上有一位名叫TextBoxA的TextBox大哥。
代码如下:
- Private Sub FormA_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
- Dim th As New System.Threading.Thread(AddressOf CrossThreadCallTest)
- th.Start()
- End Sub
- Public Sub CrossThreadCallTest()
- TextBoxA.Text = "ABC"
- End Sub
简单解释一下:
窗体加载时创建线程th执行过程CrossThreadCallTest,在线程th里面设置TextBoxA的文本。
TextBoxA是主线程创建的,在th线程里访问,造成跨线程访问Windows控件,通常您会得到类似上面的第一个提示。
错误消息如下:
在该控件上执行的操作正从错误的线程调用。使用 Control.Invoke 或 Control.BeginInvoke 封送到正确的线程才能执行此操作。
这样的问题在使用Visual Studio 2003的时候是遭遇不到的,在.NET Framework 2.0中,要想和避免同样的问题,可以采用一个简
单的方法,只要将TextBox类的CheckForIllegalCrossThreadCalls属性设置为False就可以,新代码如下
- Private Sub FormA_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
- TextBox.CheckForIllegalCrossThreadCalls = False
- Dim th As New System.Threading.Thread(AddressOf CrossThreadCallTest)
- th.Start()
- End Sub
但CheckForIllegalCrossThreadCalls并不能解决所有的问题,比如TreeView控件就没有这么幸运,通常会抛出上面第2种提示。并且
对于跨线程访问Windows控件,微软似乎并不推荐使用CheckForIllegalCrossThreadCalls,而推荐采用更为安全的访问技术。
下面这个例子是跨线程向TreeView添加节点的例子:
- Private Sub FormA_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
- Dim th As New System.Threading.Thread(AddressOf CrossThreadCallTest)
- th.Start()
- End Sub
- Public Sub CrossThreadCallTest()
- Dim delAdd As New AddNodeDelegate(AddressOf AddTreeNode)
- TreeView1.BeginInvoke(delAdd)
- End Sub
- Private Delegate Sub AddNodeDelegate()
- Private Sub AddTreeNode()
- TreeView1.Nodes.Add("ABC")
- End Sub
这样一来,AddTreeNode过程还是在主线程里运行,当然不会出现跨线程的问题了,对代码稍加改动,就能说明:
- Private Sub FormA_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
- System.Threading.Thread.CurrentThread.Name = "Main"
- Dim th As New System.Threading.Thread(AddressOf CrossThreadCallTest)
- th.Name = "th"
- th.Start()
- End Sub
- Public Sub CrossThreadCallTest()
- Dim delAdd As New AddNodeDelegate(AddressOf AddTreeNode)
- TreeView1.BeginInvoke(delAdd)
- End Sub
- Private Delegate Sub AddNodeDelegate()
- Private Sub AddTreeNode()
- TreeView1.Nodes.Add("ABC")
- '显示的应该是Main
- MessageBox.Show(System.Threading.Thread.CurrentThread.Name)
- End Sub
就写这么多,欢迎以评论的形式讨论。