WinForm 之Control.Invoke 和Control.BeginInvoke 方法的使用


Control 不能在创建它的 Thread 之外被调用。但可以通过 invoke 来保证 Control 线程安全。

在跨线程更新的时候,Control 会检查 CurrentThread 是否为创建 Control 的线程,并报错!

示例代码如下: 

ExpandedBlockStart.gif 示例代码
private   void  btnStart_Click( object  sender, EventArgs e)
{
     
// 注意:特地不使用 Timer 控件
     Thread thread  =   new  Thread(Fun);
     thread.Start(DateTime.Now.ToString());
}

// 报错:线程间操作无效: 从不是创建控件“lblTime”的线程访问它。
private   void  Fun( object  datetime)
{
     lblTime.Text 
=  ( string )datetime;
}


最简单的解决方式是在程序代码中添加如下属性:

        Control.CheckForIllegalCrossThreadCalls = false;


在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。

将要做的事情放在工作线程中执行,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的。

 

使用 BeginInvoke 方法解决该问题的代码如下:

ExpandedBlockStart.gif 使用BeginInvoke方法
using  System;
using  System.Collections.Generic;
using  System.ComponentModel;
using  System.Data;
using  System.Drawing;
using  System.Text;
using  System.Windows.Forms;
using  System.Threading;

namespace  Invoke_Test
{
    
public   partial   class  Form1 : Form
    {

        
// private System.Windows.Forms.Label lblTime;
        
// private System.Windows.Forms.Button btnStart;

        
public  Form1()
        {
            InitializeComponent();
            
//  解决方式一
            
//  Control.CheckForIllegalCrossThreadCalls = false;
        }

        
private   void  btnStart_Click( object  sender, EventArgs e)
        {
            
string  arg  =  DateTime.Now.ToString();

            
//  注意:创建子线程间接调用
            Thread thread  =   new  Thread(FunStart);
            thread.Start(arg); 
// arg 给方法传参  
        }

        
//  定义调用方法的委托
         delegate   string  FunDelegate( string  str);

        
//  注意:特地使用 FunStart 方法模拟间接调用
         private   void  FunStart( object  obj)
        {
            
//  要调用的方法的委托
            FunDelegate funDelegate  =   new  FunDelegate(Fun);

            
/* ========================================================
             * 使用this.BeginInvoke方法
             * (也可以使用this.Invoke()方法)
            ========================================================
*/

            
//  this.BeginInvoke(被调用的方法的委托,要传递的参数[Object数组])
            IAsyncResult aResult  =   this .BeginInvoke(funDelegate,obj.ToString());

            
//  用于等待异步操作完成(-1表示无限期等待)
            aResult.AsyncWaitHandle.WaitOne( - 1 );

            
//  使用this.EndInvoke方法获得返回值
             string  str  =  ( string ) this .EndInvoke(aResult);
            MessageBox.Show(str.ToString());
        }

        
//  真正需要执行的方法
         private   string  Fun( string  datetime)
        {
            lblTime.Text 
=  ( string )datetime;
            
return   " 委托的返回值 " ;
        }
    }
}


Control.InvokeRequired 属性:当前线程不是创建控件的线程时为 true。
也可以认为,在 new Control() 的时候,Control 用一个变量记录下了当前线程,在调用 InvokeRequired 时,返回当前线程是否不等于 new 的时候记录下来的那个线程。

 

Control.Invoke 和 Control.BeginInvoke 就是“发短信”的方法,如果使用 Control.Invoke 发短信,那么甲线程就会像个痴情的汉子,一直等待着乙线程的回音,而如果使用 Control.BeginInvoke 发送短信,那发完短信后,甲线程就会忙活自己的,等乙线程处理完再来瞧瞧。


推荐阅读:WinForm二三事(三)Control.Invoke&Control.BeginInvoke

示例代码Invoke_Test

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值