C#对 Windows 窗体控件进行线程安全调用 摘自MSDN

在调试器中运行应用程序时,如果一个不是创建某个控件的线程的其他线程调用该控件,则调试器会引发一个 InvalidOperationException,并显示以下消息:“从不是创建控件控件名称 的线程访问它。”

在未使用 Invoke 方法的情况下,从不是创建某个控件的线程的其他线程调用该控件是不安全的。 以下非线程安全的调用的示例。

 

代码
private void setTextUnsafeBtn_Click(
    
object sender, 
    EventArgs e)
{
    
this.demoThread = 
        
new Thread(new ThreadStart(this.ThreadProcUnsafe));

    
this.demoThread.Start();
}

private void ThreadProcUnsafe()
{
    
this.textBox1.Text = "This text was set unsafely.";
}

 

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


 

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

  1. 查询控件的 InvokeRequired 属性。

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

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

在下面的代码示例中,将在由后台线程执行的 ThreadProcSafe 方法中实现线程安全调用。 如果 TextBox 控件的 InvokeRequired 返回 true,则 ThreadProcSafe 方法会创建 SetTextCallback 的一个实例,并将该实例传递给窗体的 Invoke 方法。 这使得 SetText 方法被创建 TextBox 控件的线程调用,而且在此线程上下文中将直接设置 Text 属性。

 

代码
private void setTextSafeBtn_Click(object sender, EventArgs e)
{
    
this.demoThread = 
        
new Thread(new ThreadStart(this.ThreadProcSafe));

    
this.demoThread.Start();
}

private void ThreadProcSafe()
{
    
this.SetText("This text was set safely.");
}

private void SetText(string text)
{
    
if (this.textBox1.InvokeRequired)
    {    
        SetTextCallback d 
= new SetTextCallback(SetText);
        
this.Invoke(d, new object[] { text });
    }
    
else
    {
        
this.textBox1.Text = text;
    }
}

 

使用 BackgroundWorker 进行线程安全调用


 

在应用程序中实现多线程的首选方式是使用 BackgroundWorker 组件。 BackgroundWorker 组件使用事件驱动模型实现多线程。 后台线程运行 DoWork 事件处理程序,而创建控件的线程运行 ProgressChangedRunWorkerCompleted 事件处理程序。 可以从 ProgressChangedRunWorkerCompleted 事件处理程序调用控件。

使用 BackgroundWorker 进行线程安全调用

  1. 创建一个方法,该方法用于执行您希望在后台线程中完成的工作。 不要调用由此方法中的主线程创建的控件。

  2. 创建一个方法,用于在后台工作完成后报告结果。 可以调用由此方法中的主线程创建的控件。

  3. 将步骤 1 中创建的方法绑定到 BackgroundWorker 的实例的 DoWork 事件,并将步骤 2 中创建的方法绑定到同一实例的 RunWorkerCompleted 事件。

  4. 若要启动后台线程,请调用 BackgroundWorker 实例的 RunWorkerAsync 方法。

在下面的代码示例中,DoWork 事件处理程序使用 Sleep 来模拟需要花费一些时间完成的工作。 它不调用窗体的 TextBox 控件。 TextBox 控件的 Text 属性在 RunWorkerCompleted 事件处理程序中直接设置。

 

代码
private BackgroundWorker backgroundWorker1;
private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e)
{
    
this.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_RunWorkerCompleted(
    
object sender, 
    RunWorkerCompletedEventArgs e)
{
    
this.textBox1.Text = 
        
"This text was set safely by BackgroundWorker.";
}

 

也可以使用 ProgressChanged 事件来报告后台任务的进度。 有关包含该事件的示例,请参见 BackgroundWorker

 

示例


 

下面的代码示例是一个完整的 Windows 窗体应用程序,它包含一个带有三个按钮和一个文本框的窗体。 第一个按钮演示不安全的跨线程访问,第二个按钮演示使用 Invoke 实现的安全访问,而第三个按钮演示使用 BackgroundWorker 实现的安全访问。

 

代码
public class Form1 : Form
{
    
delegate void SetTextCallback(string text);
    
private Thread demoThread = null;
    
private BackgroundWorker backgroundWorker1;

    
private TextBox textBox1;
    
private Button setTextUnsafeBtn;
    
private Button setTextSafeBtn;
    
private Button setTextBackgroundWorkerBtn;

    
public Form1()
    {
        InitializeComponent();
    }
    
private void setTextUnsafeBtn_Click(object sender,EventArgs e)
    {
        
this.demoThread =
            
new Thread(new ThreadStart(this.ThreadProcUnsafe));

        
this.demoThread.Start();
    }

    
private void ThreadProcUnsafe()
    {
        
this.textBox1.Text = "This text was set unsafely.";
    }

    
private void setTextSafeBtn_Click(object sender,EventArgs e)
    {
        
this.demoThread =
            
new Thread(new ThreadStart(this.ThreadProcSafe));

        
this.demoThread.Start();
    }

    
private void ThreadProcSafe()
    {
        
this.SetText("This text was set safely.");
    }

    
private void SetText(string text)
    {
        {
            SetTextCallback d 
= new SetTextCallback(SetText);
            
this.Invoke(d, new object[] { text });
        }
        
else
        {
            
this.textBox1.Text = text;
        }
    }
    
private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e)
    {
        
this.backgroundWorker1.RunWorkerAsync();
    }

    
private void backgroundWorker1_RunWorkerCompleted( object sender,RunWorkerCompletedEventArgs e)
    {
        
this.textBox1.Text =
            
"This text was set safely by BackgroundWorker.";
    }
}

 

 

在运行该应用程序并单击“Unsafe Call”(不安全调用)按钮时,文本框中会立即显示“Written by the main thread”(由主线程写入)。 两秒钟后,当尝试进行不安全调用时,Visual Studio 调试器会指示出现异常。 调试器在尝试直接对文本框写入内容的后台线程中的行上停止。 您必须重新启动该应用程序才能测试其他两个按钮。 在单击“Safe Call”(安全调用)按钮时,文本框中会显示“Written by the main thread”(由主线程写入)。 两秒钟后,文本框会设置为“Written by the background thread (Invoke)”(由后台线程写入(Invoke)),这指示已调用 Invoke 方法。 在单击“Safe BW Call”(安全 BW 调用)按钮时,文本框中会显示“Written by the main thread”(由主线程写入)。 两秒钟后,文本框会设置为“Written by the main thread after the background thread completed”(在后台线程完成后由主线程写入),这指示已调用 BackgroundWorkerRunWorkerCompleted 事件的处理程序。

posted on 2010-12-03 16:22  狐狸狡猾不  阅读(1327)  评论(0编辑  收藏  举报

导航



Powered by: 博客园 copyright © 2009-2012 狐狸狡猾不