浅谈Invoke 和 BegionInvoke的用法
很多人对Invoke和BeginInvoke理解不深刻,不知道该怎么应用,在这篇博文里将详细阐述Invoke和BeginInvoke的用法:
首先说下Invoke和BeginInvoke有两种用法:
1.Control中Invoke,BeginInvoke
2.Delegate中Invokke,BeginInvoke
这两种情况是不同的,我们首先讲一下第一种,也就是Control类中的Invoke,BeginInvoke的用法。我们先来看一下MSDN是如何解释的:
control.invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托。
control.begininvoke(参数delegate)方法:在创建控件的基础句柄所在线程上异步执行指定委托。
通过官方的解释,我们大概觉得 Invoke是同步的,而BeginInvoke是异步的,至于两者的实际差别,我们通过代码来展示:
Thread invokeThread;
public delegate void invokeDelegate();
private void button1_Click(object sender, EventArgs e)
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
Console.WriteLine("AAA");
invokeThread = new Thread(new ThreadStart(StartMethod));
invokeThread.Start();
string a = string.Empty;
for (int i = ; i < ; i++) //调整循环次数,看的会更清楚
{
Thread.Sleep();
a = a + "B";
}
// MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + a); Console.WriteLine(a);
}
private void StartMethod()
{
// MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
Console.WriteLine("CCC");
button1.Invoke(new invokeDelegate(invokeMethod));
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
Console.WriteLine("DDD");
Console.Read();
} private void invokeMethod()
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE"); Console.WriteLine("EEE");
}
运行结果为:

从结果可以看出执行时首先执行AAA,然后CCC和BBB同时执行,Invoke将Invoke方法提交给主线程,主线程执行完运行在Invoke上的方法,然后才执行子线程的方法,简单讲同步就是说必须等带Invoke上的方法执行完,才能执行子线程的方法。
再来看看BeginInvokke的执行逻辑:
Thread invokeThread;
public delegate void invokeDelegate();
private void button1_Click(object sender, EventArgs e)
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
Console.WriteLine("AAA");
invokeThread = new Thread(new ThreadStart(StartMethod));
invokeThread.Start();
string a = string.Empty;
for (int i = ; i < ; i++) //调整循环次数,看的会更清楚
{
Thread.Sleep();
a = a + "B";
}
// MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + a); Console.WriteLine(a);
}
private void StartMethod()
{
// MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
Console.WriteLine("CCC");
button1.BeginInvoke(new invokeDelegate(invokeMethod));
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
Console.WriteLine("DDD");
Console.Read();
} private void invokeMethod()
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE"); Console.WriteLine("EEE");
}
运行的结果为:

从结果可以看出,首先执行AAA,然后CCC和BBB同时执行,由于采用了BeginInvoke方法,即所谓的异步执行,子线程不再等待主线程执行完成,所以DDD输出,主线程执行完BBB再执行EEE.
从运行的结果来看不管时Invoke提交的方法,还是BeginInvoke提交的方法,都是在主线上执行的,所以同步和异步的概念是相对于子线程来说的,在invoke例子中我们会发现invoke所提交的委托方法执行完成后,才能继续执行 DDD;在begininvoke例子中我们会发现begininvoke所提交的委托方法后,子线程讲继续执行DDD,不需要等待委托方法的完成。 那么现在我们在回想下invoke(同步)和begininvoke(异步)的概念,其实它们所说的意思是相对于子线程而言的,其实对于控件的调用总是由 主线程来执行的。我们很多人搞不清这个同步和异步,主要还是因为我们把参照物选错了。其实有时候光看概念是很容易理解错误的,Invoke会阻塞主支线程,BeginInvoke只会阻塞主线程,不会阻塞支线程!因此BeginInvoke的异步执行是指相对于支线程异步,而不是相对于主线程异步 。现在是不是彻底搞清楚两者的区别啦。
解决不是从创建控件的线程访问它
在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。
正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。
由于历史原因,形成了以下几种写法:
//第一种
if (this.textBox1.InvokeRequired)
{
this.textBox1.Invoke(new EventHandler( delegate { this.textBox1.Text = "测试"; }));
} //第二种
this.Invoke(new EventHandler(delegate { this.textBox1.Text = "测试"; })); //第三种
this.Invoke(new Action(() => { this.textBox1.Text = "测试"; }));
毫无疑问,第三种调用方法是最佳选择。
delegate中的Invoke方法和BeginInvoke方法
delegate中Invoke方法和BenginInvoke方法主要是用在同步调用,异步调用,异步回调上
A.同步调用,举个简单的例子:
private void button2_Click(object sender, EventArgs e)
{
Add d = new Add((a, b) =>
{
Thread.Sleep();
return a + b;
}); //实例化委托并给委托赋值 int result = d.Invoke(, ); Console.WriteLine("继续做别的事情"); Console.WriteLine(result.ToString());
Console.Read(); } public delegate int Add(int i1,int i2);
Invoke方法的参数很简单,一个委托,一个参数表(可选),而Invoke方法的主要功能就是帮助你在UI线程上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。不管当前线程是不是UI线程,Invoke都阻塞直到委托指向的方法执行完毕,然后切换回发出调用的线程(如果需要的话),返回。
所以Invoke方法的参数和返回值和调用他的委托应该是一致的
B.异步调用
举个简单的例子:
public Form1()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
//委托类型的Begininvoke(<输入和输出变量>,AsyncCallbac callback,object asyncState)方法:异步调用的核心
//第一个参数10,表示委托对应的方法实参。
//第二个参数callback,回调函数,表示异步调用结束时自动调用的函数。
//第三个参数asyncState,用于向回调函数提供相关参数信息
//返回值:IAsyncResult -->异步操作状态接口,封装了异步执行的中的参数
//IAsyncResult接口成员:查看帮助文档
Calcute objCalcute = new Calcute(ExcuteDelegate1); IAsyncResult result = objCalcute.BeginInvoke(, , null, null); //IAsyncResult是一个接口 this.textBox1.Text = "等待结果......."; this.textBox2.Text = ExcuteDelegate2(, ).ToString();
//委托类型的EndInvoke()方法:借助于IAsyncResult接口对象,不断查询异步调用是否结束。
//该方法知道被异步调用的方法所有参数,所有,异步调用完毕后,取出异步调用结果作为返回值。
this.textBox1.Text = objCalcute.EndInvoke(result).ToString();
}
public delegate int Calcute(int i1, int i2);
int ExcuteDelegate1(int i1, int i2)
{
Thread.Sleep();
return i1 + i2;
}
int ExcuteDelegate2(int i1, int i2)
{
return i1 * i2;
}
}
从运行结果可以看出运行BeginInvoke上运行的方法时不会影响 this.textBox2.Text = ExcuteDelegate2(20, 10).ToString(); 的执行,但是EndInvoke方法需要等待线程执行完毕,所以依旧会阻塞主线程的执行,为了解决这个问题,异步回调就出现了,其实可以将异步执行看成是异步回调的特殊情况
C.异步回调
看下面的例子:
public FrmCalllBack()
{
InitializeComponent(); //【3】初始化委托变量
this.objMyCal = new MyCalculator(ExecuteTask); //也可以直接使用Lambda表达式
this.objMyCal = (num, ms) =>
{
System.Threading.Thread.Sleep(ms);
return num * num;
};
}
//【3】创建委托变量(因为异步函数和回调函数都要用,所以定义成员变量)
private MyCalculator objMyCal = null; //【1】声明一个委托
public delegate int MyCalculator(int num, int ms); /// <summary>
/// 【2】根据委托定义一个方法:返回一个数的平方
/// </summary>
/// <param name="num">基数</param>
/// <param name="ms">延迟的时间:秒</param>
/// <returns></returns>
private int ExecuteTask(int num, int ms)
{
System.Threading.Thread.Sleep(ms);
return num * num;
} //【4】同时执行多个任务
private void btnExec_Click(object sender, EventArgs e)
{
for (int i = ; i < ; i++)//产生10个任务
{
//开始异步执行,并封装回调函数 objMyCal.BeginInvoke(10 * i, 1000 * i, null, i);
objMyCal.BeginInvoke( * i, * i, MyCallBack, i);
//最后一个参数 i 给回调函数的字段AsyncState赋值,如果数据很多可以定义成类或结构
}
}
//【5】回调函数
private void MyCallBack(IAsyncResult result)
{
int res = objMyCal.EndInvoke(result); //异步显示结果:result.AsyncState字段用来封装回调时自定义的参数,object类型
Console.WriteLine("第{0}个计算结果为:{1}", result.AsyncState.ToString(), res);
}
}
从运行结果看出,异步回调不再阻塞主线程的执行。注意: BeginInvoke和EndInvoke必须成对调用.即使不需要返回值,但EndInvoke还是必须调用,否则可能会造成内存泄漏。
异步编程的总结:
1. 异步编程是建立在委托基础上的一种编程方法。
2. 异步调用的每个方法都是在独立的线程中执行。因此,本质上就是一种多线程程序,也可以说是一种“简化版本”的多线程技术。
3. 比较适合在后台运行较为耗费时间的"简单任务",并且任务要求相互独立,任务中不应该有代码直接访问可视化控件。
4. 如果后台任务要求必须按照特定顺序执行,或者必须访问共享资源,则异步编程不适合,而应该直接采用多线程开发技术。
浅谈Invoke 和 BegionInvoke的用法的更多相关文章
- VC++ 浅谈VS2010中CMFCToolBar的用法
本文将给大家介绍Visual Studio 2010中CMFCToolBar的用法,CMFCToolBar可以让用户自定义工具栏图标,使用静态成员函数SetUserImages()将一个CMFCToo ...
- 浅谈dynamic的简单使用用法
今天看了博客园里面的dynamic用法,我犹豫从来没接触过,今天恶补了一下,把我对dynamic的认识分享了出来,大家一起学习. Visual C# 2010 引入了一个新类型 dynamic. 该类 ...
- 浅谈Sql各种join的用法
1.left join.right join.inner join三者区别 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右 ...
- 浅谈Spring框架注解的用法分析
原文出处: locality 1.@Component是Spring定义的一个通用注解,可以注解任何bean. 2.@Scope定义bean的作用域,其默认作用域是”singleton”,除此之外还有 ...
- 由jtable浅谈vector<vector<Object>>的用法(转自a718515028的专栏)
以前只用过vector<Object> ,但是在做从数据库导出数据放到jtable中时,发现还有个vector<vector<Object>>的用法. 先说jta ...
- 浅谈css常用伪类用法
着重写一下after和before的用法: css样式搞定:标签元素+伪类after a.'class名':after{//我的样式名称是这个,可以写成你自己的样式名称 content: '已打包'; ...
- 浅谈JS中 reduce() 的用法
过去有很长一段时间,我一直很难理解 reduce() 这个方法的具体用法,平时也很少用到它.事实上,如果你能真正了解它的话,其实在很多地方我们都可以用得上,那么今天我们就来简单聊聊JS中 reduce ...
- 浅谈移动端rem的用法
一 什么是rem? “font size of the root element 这是w3c的定义 也就是说是相对于根节点(html节点)的字体大小的单位. 目前主流的浏览器基本都支持rem这个单位, ...
- 浅谈mmap()和ioremap()的用法与区别
一.mmap()mmap()函数是用来将设备内存线性地址映射到用户地址空间.(1)首先映射基地址,再通过偏移地址寻址:(2)unsigned char *map_cru_base=(unsigned ...
随机推荐
- SQL Server 2017 正式发布:同时支持 Windows 和 Linux(现在看下来,当年那德拉的“云优先,移动优先”是有远见的,而且是有一系列的措施和产品相配合的,只是需要一点时间而已。真是佩服!!)
微软在去年 3 月首次对外宣布了 Linux 版的 SQL Server,并于今年 7 月发布了首个公开 RC 版.前几日在美国奥兰多召开的微软 Ignite 2017 大会上,微软宣布 SQL Se ...
- HDU 4279 Number(2012天津网络游戏---数论分析题)
转载请注明出处:http://blog.csdn.net/u012860063? viewmode=contents 题目链接:pid=4279">http://acm.hdu.edu ...
- 执行xcopy命令后出现Invalid num of parameters错误的解决办法
作者:朱金灿 来源:http://blog.csdn.net/clever101 在执行一条批处理命令: xcopy /s /i /y C:\ppt D:\Program doc 开始很纳闷,上网一查 ...
- 通通玩blend美工(8)——动态绘制路径动画,画出个萌妹子~
原文:通通玩blend美工(8)--动态绘制路径动画,画出个萌妹子~ 2年前我在玩Flex的时候就一直有一个疑问,就是如何来实现一个蚊香慢慢烧完的Loading动画呢? 刚经历了某甲方高强度一个月的洗 ...
- WPF 动态模拟CPU 使用率曲线图
原文:WPF 动态模拟CPU 使用率曲线图 在工作中经常会遇到需要将一组数据绘制成曲线图的情况,最简单的方法是将数据导入Excel,然后使用绘图功能手动生成曲线图.但是如果基础数据频繁更改, ...
- WPF TreeView HierarchicalDataTemplate
原文 WPF TreeView HierarchicalDataTemplate HierarchicalDataTemplate 的DataType是本层的绑定,而ItemsSource是绑定下层的 ...
- 使用sklearn构建含有标量属性的决策树
网络上使用sklearn生成决策树的资料很多,这里主要说明遇见标量数据的处理. 经查验参考资料,sklearn并非使用了课上以及书上讲的ID3算法,而是选择了CART,该算法生成二叉树:scikit- ...
- JAVA 与 PHP 的不同和相同
Java语言与PHP语言因为都可以做web应用开发,所以总有入门学习这2种语言的入门者会问Java和PHP哪一个好.讨论语言的好坏是一件不太明智的事情,我认为只能去说那一种编程语言更加适合干什么,人与 ...
- Windows 10 UWP开发:如何去掉ListView默认的选中效果
原文:Windows 10 UWP开发:如何去掉ListView默认的选中效果 开发UWP的时候,很多人会碰到一个问题,就是ListView在被数据绑定之后经常有个默认选中的效果,就像这样: 而且它不 ...
- Markdown的选择
直击现场 我一直在思索用什么格式存储文档比较好.之前一直在用google docs,但是它的格式不公开,上传/下载的时候需要转换格式,转换的时候必然会丢失一些信息.于是后来想,那还是纯本文或者mark ...