【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)
今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别。所以花了点时间研究了下。
据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的。
- Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
- Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
msdn说明:
控件上的大多数方法只能从创建控件的线程调用。 如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从任何线程上安全调用的方法,它们是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。 在后台线程上创建控件的句柄之前调用 CreateGraphics 可能会导致非法的跨线程调用。 对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。 调用方法始终在控件的线程上调用自己的回调。
于是用下面的代码进行初步的测试:
1.主线程调用Invoke
1 /// <summary> 2 /// 直接调用Invoke 3 /// </summary> 4 private void TestInvoke() 5 { 6 listBox1.Items.Add("--begin--"); 7 listBox1.Invoke(new Action(() => 8 { 9 listBox1.Items.Add("Invoke"); 10 })); 11 12 Thread.Sleep(1000); 13 listBox1.Items.Add("--end--"); 14 }
输出:
从输出结果上可以看出,Invoke被调用后,是马上执行的。这点很好理解。
2.主线程调用BeginInvoke
1 /// <summary> 2 /// 直接调用BeginInvoke 3 /// </summary> 4 private void TestBeginInvoke() 5 { 6 listBox1.Items.Add("--begin--"); 7 var bi = listBox1.BeginInvoke(new Action(() => 8 { 9 //Thread.Sleep(10000); 10 listBox1.Items.Add("BeginInvoke"); 11 })); 12 Thread.Sleep(1000); 13 listBox1.Items.Add("--end--"); 14 }
输出:
从输出能看出,只有当调用BeginInvoke的线程结束后,才执行它的内容。
不过有两种情况下,它会马上执行:
使用EndInvoke,检索由传递的 IAsyncResult 表示的异步操作的返回值。
/// <summary>/// 调用BeginInvoke、EndInvoke/// </summary>private void TestBeginInvokeEndInvoke(){listBox1.Items.Add("--begin--");var bi = listBox1.BeginInvoke(new Action(() =>{Thread.Sleep(1000);listBox1.Items.Add("BeginInvokeEndInvoke");}));listBox1.EndInvoke(bi);listBox1.Items.Add("--end--");}
输出:
同一个控件调用Invoke时,会马上执行先前的BeginInvoke
/// <summary>/// 调用BeginInvoke、Invoke/// </summary>private void TestBeginInvokeInvoke(){listBox1.Items.Add("--begin--");listBox1.BeginInvoke(new Action(() =>{Thread.Sleep(1000);listBox1.Items.Add("BeginInvoke");}));listBox1.Invoke(new Action(() =>{listBox1.Items.Add("Invoke");}));listBox1.Items.Add("--end--");}
输出:
注:在主线程中直接调用Invoke、BeginInvoke、EndInvoke都会造成阻塞。所以应该利用副线程(支线线程)调用。
3.支线线程调用Invoke
创建一个线程,并在线程中调用Invoke,同时测试程序是在哪个线程中调用Invoke。
1 /// <summary> 2 /// 线程调用Invoke 3 /// </summary> 4 private void ThreadInvoke() 5 { 6 listBox1.Items.Add("--begin--"); 7 new Thread(() => 8 { 9 Thread.CurrentThread.Name = "ThreadInvoke"; 10 string temp = "Before!"; 11 listBox1.Invoke(new Action(() => 12 { 13 Thread.Sleep(10000); 14 this.listBox1.Items.Add(temp +="Invoke!" + Thread.CurrentThread.Name); 15 })); 16 temp += "After!"; 17 }).Start(); 18 listBox1.Items.Add("--end--"); 19 } 20 21 22 private void button1_Click(object sender, EventArgs e) 23 { 24 Thread.CurrentThread.Name = "Main"; 25 ThreadInvoke(); 26 } 27 28 private void button2_Click(object sender, EventArgs e) 29 { 30 listBox1.Items.Add("button2_Click"); 31 }
输出:
- Invoke的委托在主线程中执行
- Invoke在支线程中调用也会阻塞主线程。(当点击button1后,我试图去点击button2,却发现程序被阻塞了)
- Invoke还会阻塞支线程。(因为输出结果中没有出现After)
接着来测试下在支线程中调用BeginInvoke.
4.支线线程调用BeginInvoke
1 /// <summary> 2 /// 线程调用BeginInvoke 3 /// </summary> 4 private void ThreadBeginInvoke() 5 { 6 listBox1.Items.Add("--begin--"); 7 new Thread(() => 8 { 9 Thread.CurrentThread.Name = "ThreadBeginInvoke"; 10 string temp = "Before!"; 11 listBox1.BeginInvoke(new Action(() => 12 { 13 Thread.Sleep(10000);14 this.listBox1.Items.Add(temp += "Invoke!" + Thread.CurrentThread.Name); 15 })); 17 temp += "After!"; 18 }).Start(); 19 listBox1.Items.Add("--end--"); 20 } 21 22 23 private void button1_Click(object sender, EventArgs e) 24 { 25 Thread.CurrentThread.Name = "Main"; 26 ThreadBeginInvoke(); 27 } 28 29 private void button2_Click(object sender, EventArgs e) 30 { 31 listBox1.Items.Add("button2_Click"); 32 }
输出:
- BeginInvoke在主线程中执行。
- BeginInvoke在支线程中调用也会阻塞主线程。
- BeginInvoke相对于支线程是异步的。
总结:
以下为了方便理解,假设如下:
主线程表示Control.Invoke或Control.BeginInvoke中Control所在的线程,即创建该创建的线程。(一般为UI线程)
支线程表示不同于主线程的调用Invoke或BeginInvoke的线程。
- Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行。(也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在主线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死)
- Invoke会阻塞主支线程,BeginInvoke只会阻塞主线程,不会阻塞支线程!因此BeginInvoke的异步执行是指相对于支线程异步,而不是相对于主线程异步。(从最后一个例子就能看出,程序运行点击button1)
SamWang
2012-05-25
作者:SamWang
出处:http://wangshenhe.cnblogs.com/
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。
关注 - 5
粉丝 - 160
» 下一篇:[转]VS隐藏的快捷键和小功能
评论:
假如这里所谓的“主线程”是指创建控件的线程。所谓“支线程”是指不同于主线程而且是调用begininvoke/invoke的线程。
beginInvoke和invoke所执行的委托是在主线程中执行的。但invoke会阻塞线程,beginInvoke则不会。他们将到windows消息队列中排队等待。所以beginInvoke之后,该委托将和支线程之后的代码并发执行,而不存在你所说的先后问题。该委托在主线程中执行所以也不存在什么阻塞主线程这个说法。
至于在运行结果中出现了 Before!After!Main ,这并能说明BeginInvoke的委托一定是在线程结束以后执行。出现这个结果的主要原因在于 引用Thread.Sleep(1000);这句代码,让主线程睡眠了一秒钟。正如前面所说,委托在主线程windows消息队列中,主线程睡眠,它当然要等一秒后,直到当前事件button1_Click执行完了,才能可能轮到他。而支线程恰好用着一秒中时间执行完了。所以让你误认为是beginInvoke回调的委托肯定是在支线程执行完后才会执行。你可以在 引用temp += "After!";之前加Thread.Sleep(10000);结果很可能就是Before!Main!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
public IAsyncResult BeginInvoke(Delegate method, params object [] args)
{
IAsyncResult result;
using ( new Control.MultithreadSafeCallScope())
{
Control control = this .FindMarshalingControl();
result = (IAsyncResult)control.MarshaledInvoke( this , method, args, false );
}
return result;
}
// System.Windows.Forms.Control
public object Invoke(Delegate method, params object [] args)
{
object result;
using ( new Control.MultithreadSafeCallScope())
{
Control control = this .FindMarshalingControl();
result = control.MarshaledInvoke( this , method, args, true );
}
return result;
}
|
再细看一下MarshaledInvoke代码就知道都是做了些什么
【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)相关推荐
- 浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别
今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别.所以花了点时间研究了下. 据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的. Cont ...
- 【读书笔记】舆情分析浅谈:读《论新媒体发展背景下网络舆情的发展及治理》有感
舆情分析浅谈 读<论新媒体发展背景下网络舆情的发展及治理>有感 最近读了一些有关"舆情"的论文,其中印象比较深的有一篇<论新媒体发展背景下网络舆情的发展及治理&g ...
- 委托的Invoke 和 BeginInvoke 与Control的Invoke和BeginInvoke(转-因为写得很好)
原文地址:http://www.cnblogs.com/worldreason/archive/2008/06/09/1216127.html Invoke and BeginInvoke Invok ...
- C# Control的Invoke和BeginInvoke
之前在项目中遇到了UI界面更新出错的问题,后来在网上找了很多资料,终于解决,先将资料整理如下: 为什么需要Control.Invoke和Control.BeginInvoke?? 如果从另外一个线程操 ...
- 浅谈计算机教学的现状分析,浅谈中职计算机专业教学的现状与对策
好文网为大家准备了关于浅谈中职计算机专业教学的现状与对策的文章,好文网里面收集了五十多篇关于好浅谈中职计算机专业教学的现状与对策好文,希望可以帮助大家.更多关于浅谈中职计算机专业教学的现状与对策内容请 ...
- 高通android逆向分析,浅谈Android高通(Qualcomm)和联发科(MTK)平台
一款CPU好不好是要从多个方面考虑的,并不是说简简单单看一个主频.几个核心数就完了,更重要的是它的综合实力到底有多强,这里面当然也会牵扯到价格问题,性能相似当然是便宜的获胜,这是毋庸置疑的. 事实上, ...
- Web日志安全分析浅谈
一.为什么需要对日志进行分析? 随着Web技术不断发展,Web被应用得越来越广泛,所谓有价值的地方就有江湖,网站被恶意黑客攻击的频率和网站的价值一般成正比趋势,即使网站价值相对较小,也会面对" ...
- Java父类与子类中静态代码块 实例代码块 静态变量 实例变量 构造函数执行顺序...
实例化子类时,父类与子类中的静态代码块.实例代码块.静态变量.实例变量.构造函数的执行顺序是怎样的? 代码执行的优先级为: firest:静态部分 second:实例化过程 详细顺序为: 1.父类静态 ...
- ue4中在物体上加ui_UE4 物体位置同步相关源码分析浅谈
前言 多图, 不想在源代码写注释, 不想贴代码块, 看的不清楚 版本4.21混4.22, 区别不大 文章属于旧有文章搬运, 之前在csdn上面 2019.10.27修改一版 物体位置信息同步, 或者说 ...
最新文章
- python定义一个空数组_python – 在numpy数组中设置空值
- 富士通服务器安装操作系统,富士通平板u盘安装系统教程
- 由胡润百富榜联想到的
- Tornado帮助文档组织(zt)
- 遍历HashMap的四种方法
- mysql+if+x+mod+2_Windows 下 MantisBT 2.X + Apache 2.4 + PHP 7 + MySQL 5.7 的环境配置
- lgb,xgb,gbdt,adb,RF区别与联系
- Git命令学习总结(-)
- Zend Studio 7.2 汉化包 及安装方法
- pytorch搭建TextRNN与使用案例
- 小程序跳转样式布局错乱_小程序页面布局样式元素总结
- ES6——变量的解构赋值
- 桥接模式和php实现
- MyEclipse配色
- ucGUI移植到ARM
- 小米路由器开启SSH并连接
- PI控制器概念 笔记
- Linux 的日志服务器 详解(Rsyslog)
- PHP获取唯一标识UUID
- 大屏电视开启观影新体验,震撼视听双重享受
热门文章
- android 串口一直打开_实例 | 使用Python串口实时显示数据并绘图
- 40+张最全Linux/C/C++思维导图,收藏!
- linux怎么不自动登录,linuxdeepin 如何取消自动登录啊?
- FPGA实现A5算法并仿真
- sqlserver 人名_sqlserver上机试题
- 测试开发python面试_python测试开发面试之深浅拷贝
- 学python编程_少儿学Python编程的一些思考
- 数组序列化 java_Java基础之数组序列化、反序列化 小发现(不知道 是不是有问题)...
- centos ezhttp mysql_Windows 访问CentOS上Mysq配置说明
- html font-family设置无效,css设置中文字体(font-family:黑体)后样式失效问题