使用多线程提高 Windows 窗体应用程序的性能时,必须注意以线程安全方式调用控件。
        访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。
下面的代码示例演示如何从辅助线程以线程安全方式和非线程安全方式调用 Windows 窗体控件。它演示一种以非线程安全方式设置 TextBox 控件的 Text 属性的方法,还演示两种以线程安全方式设置 Text 属性的方法。

使用 BackgroundWorker 进行的线程安全调用

在应用程序中实现多线程的首选方式是使用 BackgroundWorker 组件。BackgroundWorker 组件使用事件驱动模型实现多线程。辅助线程运行 DoWork 事件处理程序,创建控件的线程运行 ProgressChangedRunWorkerCompleted 事件处理程序。注意不要从 DoWork 事件处理程序调用您的任何控件。

对 Windows 窗体控件进行线程安全调用

  1. 查询控件的 InvokeRequired 属性。

  2. 如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke

  3. 如果 InvokeRequired 返回 false,则直接调用控件。

// This event handler creates a thread that calls a 
// Windows Forms control in a thread-safe way.
private void setTextSafeBtn_Click(
    
object sender, 
    EventArgs e)
{
    
this.demoThread = 
        
new Thread(new ThreadStart(this.ThreadProcSafe));

    
this.demoThread.Start();
}


// This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{
    
this.SetText("This text was set safely.");
}


// This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control. 
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly. 

private void SetText(string text)
{
    
// InvokeRequired required compares the thread ID of the
    
// calling thread to the thread ID of the creating thread.
    
// If these threads are different, it returns true.
    if (this.textBox1.InvokeRequired)
    
{    
        SetTextCallback d 
= new SetTextCallback(SetText);
        
this.Invoke(d, new object[] { text });
    }

    
else
    
{
        
this.textBox1.Text = text;
    }

}
2008年3月16日
posted on 2008-03-16 20:56  冯帅  阅读(482)  评论(0)    收藏  举报