C#学习笔记之 invoke与BeginInvoke
前段时间在写C#的上位机,用到invoke和BeginInvoke。前面对这两个的用法和原理比较模糊,这几天参考了网上的一些资料,整理如下笔记。
1.1 invoke与BeginInvoke介绍
invoke与BeginInvoke的两种使用情况:
1.control中的invoke、BeginInvoke
2.delegrate中的invoke、BeginInvoke
这两种情况是不同的,这里主要介绍第一种。dotNET中对invoke和BeginInvoke的官方定义如下:
control.invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托
control.BeginInvoke(参数delegate)方法:在创建此控件的基础窗口句柄的线程上异步执行指定的委托
其中Control中的invoke和BeginInvoke的参数为delegate,委托的方法是在Control的线程上执行的,即UI线程。由定义我们可以知道invoke表示同步、Begininvoke表示异步
下面做一个测试:
新建一个UI工程,界面如下图1
1.2 invoke分析:
invoke代段码如下:
private void invoke_btn_Click(object sender, EventArgs e)
{
//主线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
invokeThread = new Thread(new ThreadStart(StartMethod));
invokeThread.Start();
string ch = string.Empty;
for(int ss = 0; ss < 3; ss++)
{
Thread.Sleep(1000);
ch = ch + "B";
}
//主线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + ch);
}
private void StartMethod()
{
//子线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
//提交到主线程
invoke_btn.Invoke(new InvokeDelegate(invokeMethod));
//子线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
}
private void invokeMethod()
{
//主线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}
测试结果:双击运行exe,单击invoke,程序运行的界面:1AAA->3CCC和1BBB->1EEE->3DDD。(Debug模式下的HashCode可能不一样,但是运行的顺序是一样的)
结果分析:单击invoke按钮后,执行invoke_btn_Click函数,主线程运行AAA,然后BBB和子线程CCC同时执行(这里用循环+延时,显示更加清楚),接着通过invoke来将invokeMethod方法提交给主线程,然后子线程等待主线程执行完毕(即等待EEE执行完成),最后执行子线程的DDD。
1.3 BeginInvoke分析:
BeginInvoke代段码如下:
private void bginvoke_btn_Click(object sender, EventArgs e)
{
//主线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
bginvokeThread = new Thread(new ThreadStart(bgStartMethod));
bginvokeThread.Start();
string ch = string.Empty;
for (int ss = 0; ss < 3; ss++)
{
Thread.Sleep(1000);
ch = ch + "B";
}
//主线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + ch);
}
private void bgStartMethod()
{
//子线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
//提交给主线程
bginvoke_btn.BeginInvoke(new InvokeDelegate(bginvokeMethod));
//子线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
}
private void bginvokeMethod()
{
//主线程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}
测试结果:双击运行exe,单击BeginInvoke,程序运行的界面:1AAA->3CCC和1BBB->1EEE和3DDD。(Debug模式下的HashCode可能不一样,但是运行的顺序是一样的)
结果分析:单击BeginInvoke按钮后,执行bginvoke_btn_Click函数,主线程运行AAA,然后BBB和子线程CCC同时执行,然后通过BeginInvoke来将bginvokeMethod方法提交给主线程,接着主线程执行EEE(主线程自己的任务完成),同时子线程继续执行DDD。
1.4对比分析
通过上述两个测试可以发现:invoke和BeginInvoke提交的委托方法都是在主线程中执行的。但是invoke所提交的委托方法是(EEE)执行完毕后,才继续执行的DDD;而BeginInvoke提交的委托方法后,子线程可以继续执行DDD,不需要等待EEE执行完毕。因此在两者的使用方面是不同的,当后台线程在更新一个UI控件的状态后不需要等待,而是继续往下执行,此时宜用BeginInvoke来进行处理,如一边接收数据,将数据存入队列,一边对数据队列进行文本,曲线的更新;当后台线程需要操作UI控件,并且需要等待该操作执行完毕才能继续往下执行,此时宜用invoke来进行处理,如必须接收一帧立即处理一帧数据。