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

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

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

 

ExpandedBlockStart.gif 代码
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 属性。

 

ExpandedBlockStart.gif 代码
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 事件处理程序中直接设置。

 

ExpandedBlockStart.gif 代码
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 实现的安全访问。

 

ExpandedBlockStart.gif 代码
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 事件的处理程序。

转载于:https://www.cnblogs.com/yuanfan/archive/2010/12/03/1895596.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值