日期:2014-05-17 浏览次数:21292 次
????? 当我们需要处理大量数据时,为了使UI界面不致出现假死状态,我们就必须使用多线程进行处理。所以问题就出现了,我们都知道线程作为一个独立运行的单元,线程间不可以随意访问和修改,那么该怎么办呢?其实C#提供了跨线程访问的方法,也就是通过委托安全调用从非拥有控件的线程访问控件。
??????? 一、委托??????? 我们首先先来了解下委托,简单地说,委托就是一个类,它定义了方法传递参数的类型和个数,使得我们可以把方法作为参数进行传递,使得程序具有更好的扩展性。如果大家还不明白的话,我们可以举个例子:
private delegate void setTextDelegate( string msg); //声明一个委托类型,这个委托类型传递一个string类型参数,并返回为void private void setLabelText(string value) //这是一个传递string类型参数,并返回void的方法 { this.label1.Text = value; } private void setTextboxTex(string msg) //这也是一个传递string类型参数,并返回void的方法 { this.textBox1.Text = msg; } private void setText(string msg,setTextDelegate std) //把setTextDelegate类型委托作为一个参数进行传递 { std(msg); //真正的参数传递 } private void button1_Click(object sender, EventArgs e) { this.setText("这是label控件", setLabelText); //因为setLabelText传递的参数类型和返回值都和委托声明的类型一致,所以可以进行传递 this.setText("这是textBox控件", setTextboxTex); //同上 }
??? 总结下,从上面的例子我们可以清楚地看到,只要方法传递的参数类型、个数和返回值与委托声明的一致,我们就可以把该方法作为参数进行传递。
??? 我们也可以将多个方法绑定到委托上,所有绑定上去的方法就会形成一个链表顺序执行。
private void button1_Click(object sender, EventArgs e) { setTextDelegate std = new setTextDelegate(setLabelText); //实例化委托并绑定setLabelText方法 std += setTextboxTex; //使用"+="号绑定方法,解绑使用"-="号 this.setText("这是控件", std); }
?? ?? 这样setTextDelegate绑定了两个方法,当我们this.setText("这是控件", std);时两个方法会顺序执行,这样做大大提高了程序的可扩性。
?????? 在讲解跨线程委托调用的方法前,我们先了解几个常用的方法和属性:
?????? 1.Control.InvokeRequired属性(Control是所有控件的基类),这个属性用来判断Control控件是否为调用线程创建的,如果为否的话,也就是创建Control控件的线程不是调用线程,返回false,否则返回true。
????? 2.Control.Invoke() 方法,这是同步调用的方法,它顺序执行Invoke(Delegate)里的委托方法会再继续执行下面的方法,下面会详细解释。
????? 3.Control.BeginInvoke()方法,这是异步调用的方法,它会类似于把委托内的方法又创建了一条线程来执行,只有调用线程进行Sleep切换时才会执行委托方法,下面会详细解释它和Control.Invoke()方法的区别。
????? 先来看个例子:
private delegate void setTextDelegate( int value); //先声明一个传递int类型参数,并返回为void的委托 private void button1_Click(object sender, EventArgs e) { Thread newThread = new Thread(new ThreadStart(threadHandler)); ?newThread.Start(); } private void threadHandler() { for(int i =0 ; i <=100 ; i ++) { this.UIHandler(i); Thread.Sleep(100); } } ?private void UIHandler(int value) { if(this.label1.InvokeRequired) //判断label1控件是否是调用线程(即newThread线程)创建的,也就是是否跨线程调用,如果是则返回true,否则返回false { this.label1.BeginInvoke(new setTextDelegate(setLabelText),new object []{ value}); //异步调用setLabelText方法,并传递一个int参数 } else { this.label1.Text = value.ToString() + "%"; } } private void setLabelText(int value) //当跨线程调用时,调用该方法进行UI界面更新 { this.label1.Text = value.ToString() + "%"; }
???? 这是一个简单的跨线程调用的例子,不懂的可以把例子拷贝自己运行下就清楚了,其实原理很简单,分两步就搞定了:
??? 1. 声明一个委托类型,定义它需要传递的参数类型、个数和返回的类型。
??? 2.判断是否跨线程调用更新控件内容(Control.InvokeRequired),如果是的话,就要用Invoke()或BeginInvoke()执行委托。
???? 三、Control.Invoke()与Control.BeginInvoke()方法的区别???? 我们都知道这两个方法都可以进行委托执行,但其中Control.Invoke()是同步调用执行,Control.BeginInvoke()是异步调用执行。
???? MSDN上的解释是这样的:
???? Control.Invoke()方法:在拥有此控件的基础窗口句柄的线程上执行委托。?? ? Contr