【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)

  今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别。所以花了点时间研究了下。

  据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的。

  • Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
  • Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。

msdn说明:

控件上的大多数方法只能从创建控件的线程调用。 如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从任何线程上安全调用的方法,它们是:InvokeBeginInvokeEndInvoke 和 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/ 
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。

【分析】浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别(SamWang)的更多相关文章

  1. C#中Control的Invoke和BeginInvoke是相对于支线线程

    近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下. Control的Invoke和BeginInvoke 是相对于支线线程(因为一般在支线线程中调用, ...

  2. [转载]Winform中Control的Invoke与BeginInvoke方法

    转自http://www.cppblog.com/baby-fly/archive/2010/04/01/111245.html 一.为什么 Control类提供了 Invoke和 BeginInvo ...

  3. Control的Invoke和BeginInvoke详解

    (一)Control的Invoke和BeginInvoke 我们要基于以下认识: (1)Control的Invoke和BeginInvoke与Delegate的Invoke和BeginInvoke是不 ...

  4. [转]Control的Invoke和BeginInvoke

    转自:Control的Invoke和BeginInvoke  作者:Kuffy Wang 近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下.感谢这篇文 ...

  5. C#委托同步异步说明,并比较control调用Invoke和BeginInvoke的异同

    一.委托的同步和异步: 1.同步 使用Invoke调用同步,或直接写fun1("func"),在fun1.Invoke这一步会明显的阻塞线程 使用: static void Mai ...

  6. Control的Invoke和BeginInvoke

    转载:https://www.cnblogs.com/c2303191/articles/826571.html 近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资 ...

  7. Java 中(静态)变量、(静态)代码块的执行顺序

    Java 中(静态)变量.(静态)代码块的执行顺序 非原创 本文讨论 Java 中(静态)变量.(静态)代码块的执行顺序 首先创建 3 个类 1.Foo 类 public class Foo { pu ...

  8. 浅谈c#的三个高级参数ref out 和Params C#中is与as的区别分析 “登陆”与“登录”有何区别 经典SQL语句大全(绝对的经典)

    浅谈c#的三个高级参数ref out 和Params   c#的三个高级参数ref out 和Params 前言:在我们学习c#基础的时候,我们会学习到c#的三个高级的参数,分别是out .ref 和 ...

  9. 技术分析 | 浅谈在MySQL体系下SQL语句是如何在系统中执行的及可能遇到的问题

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 SQL语句大家并不陌生,但某种程度上来看,我们只是知道了这条语句是什么功能,它可 ...

随机推荐

  1. Drools Expression 介绍

    用好Drools 中的表达式是你的规则引擎能否强大的必要条件 http://docs.jboss.org/drools/release/6.1.0.Final/drools-docs/html_sin ...

  2. CSS基础语法和CSS经常用到的知识点总结

    1. [代码]css基础教程     CSS基础语法CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明.每条声明由一个属性和一个值组成.每个属性有一个值.属性和值被冒号分开.例如:下面这行代 ...

  3. POJ 1639 Picnic Planning:最小度限制生成树

    题目链接:http://poj.org/problem?id=1639 题意: 给你一个无向图,n个节点,m条边,每条边有边权. 让你求一棵最小生成树,同时保证1号节点的度数<=k. 题解: 最 ...

  4. Cuckoo hash算法分析——其根本思想和bloom filter一致 增加hash函数来解决碰撞 节省了空间但代价是查找次数增加

    基本思想: cuckoo hash是一种解决hash冲突的方法,其目的是使用简单的hash 函数来提高hash table的利用率,同时保证O(1)的查询时间 基本思想是使用2个hash函数来处理碰撞 ...

  5. python-多线程趣味(锁)

    接上一篇,程序员在敲代码的时候觉得无聊,无聊的时候,会想到去吃零食,那么假如一个函数: #! /usr/bin/env python #coding=utf-8 ''' ''' import time ...

  6. jmeter--轻量级接口自动化测试框架

    大致思路: jmeter完成接口脚本,Ant完成脚本执行并收集结果生成报告,最后利用jenkins完成脚本的自动集成运行. 环境安装: 1.jdk1.7 配置环境变量(参考前面的分页) 2.jmete ...

  7. Javascript-- jQuery DOM篇(二)

    DOM拷贝clone() 克隆节点是DOM的常见操作,jQuery提供一个clone方法,专门用于处理dom的克隆 .clone()方法深度 复制所有匹配的元素集合,包括所有匹配元素.匹配元素的下级元 ...

  8. 关于COM组件调用

    转载自:http://www.cppblog.com/ice197983/articles/4178.html 一.调用步骤: 使用ATL编写的COM组件调用方法有两种:1.导入myCom.dll文件 ...

  9. django学习笔记(三)模型

    1.创建一个django app: python manage.py startapp books 2.validate 命令检查你的模型的语法和逻辑是否正确.一旦你觉得你的模型可能有问题,运行 py ...

  10. u盘安装ubuntu 12.04 server问题解决

    问题: 使用UltraISO 9.5.3制作U盘启动盘,ISO文件使用ubuntu-12.04.2-server-i386.iso,ISO文件经过MD5验证是正确的. 将U盘查到计算机上,进bios选 ...