C#: Control中的Invoke的使用。Invoke和BeginInvoke一般是在支路线程中调用,用来更新主界面。Invoke在拥有此控件的基础窗口句柄的线程上执行指定的委托;BeginInvoke在创建控件的基础句柄的线程上异步执行指定的委托。
Invoke的调用:
using System;
using System.Threading;
using System.Windows.Forms;
namespace test20180818
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Thread invokeThread;
private delegate void InvokeDelegate();
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "1");
invokeThread = new Thread(new ThreadStart(StartMethod));
invokeThread.Start();
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"2");
}
private void StartMethod()
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"3");
button1.Invoke(new InvokeDelegate(invokeMethod));
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"4");
}
private void invokeMethod()
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"5");
}
}
}
执行的结果为:1-》并行的结果2和3-》5-》4
1------>2和StartMethod()中的3同时得到----->3执行完,不管2有没有执行完,InvokeThread把消息封送(Invoke)给UI线程,然后自己等待)---->UI线程处理完button1_Click后,处理InvokeThread封送过来的消息,执行InvokeMethod方法,即5,处理往后UI线程切换到invokeThread线程,最后得到4。
BeginInvoke的写法同上,把Invoke替换成BeginInvoke即可测试结果。
using System;
using System.Threading;
using System.Windows.Forms;
namespace test20180818
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Thread beginInvokeThread;
private delegate void beginInvokeDelegate();
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "1");
beginInvokeThread = new Thread(new ThreadStart(StartMethod));
beginInvokeThread.Start();
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"2");
}
private void StartMethod()
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"3");
button1.BeginInvoke(new InvokeDelegate(beginInvokeMethod));
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"4");
}
private void beginInvokeMethod()
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"5");
}
}
}
1在UI线程上执行----->beginInvokeThread线程开始执行,UI继续执行代码段2,并发地invokeThread执行代码段3-------------->不管UI有没有执行完代码段2,这时beginInvokeThread线程把消息封送给UI,自己并不等待,继续向下执行-------->UI处理完button1_Click消息后,处理beginInvokeThread线程封送过来的消息。
能够区分他们分别是执行在哪个线程上的。有线程UI/线程begininvokeThread/线程invokeThread
注:以上1、2、3、4、5分别是输出打印的字符所在段落的代码
C#为控件单独开辟了一个线程,当另外一个线程的方法需要修改控件或者调用控件的方法时,需要通过控件的InvokeRequired方法来进行。比如,当另一个线程想调用控件的方法时:
//定义委托
private delegate void SendCallBack(List<byte[]> bufferList,bool feedback);
//Send方法属于某个窗口
private void Send(List<byte[]> bufferList,bool feedback) {
if (this.InvokeRequired) { //跨线程调用时的执行逻辑
try {
SendCallBack cb = new SendCallBack(Send);
this.Invoke(cb,bufferList,feedback);
} catch(Exception ex) {
MessageBox.Show(ex.Message);
}
}
else
{
//不是跨线程调用此方法时的执行逻辑
} }
private void serialPort1_DataReceived(object sender,SerialDataReceivedEventArgs e) {
Send(bufferlist,true);
}
控件的InvokeRequired属性:bool值,为true时表示调用Send方法的是另一个线程,此时需要将Send方法传送给一个委托让委托所在的线程来代替执行Send方法;为false时表示Send的调用者没有跨线程调用Send方法,此时直接执行else中的代码即可。
串口的DataReceived事件和Send方法所属的窗口不在同一个线程,因此在serialPort1_DataReceived事件中调用Send方法时就会执行Send方法中if块中的代码。
this.InvokeRequired
说白了,判断当前线程是否是UI线程。
如果this.InvokeRequired = true,你需要通过this.Invoke()传递委托去操作界面。否则直接操作即可。