【分析】浅谈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/ 
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。

原文地址:https://www.cnblogs.com/asdyzh/p/9858980.html

时间: 2024-08-04 03:19:24

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

浅谈C#中Control的Invoke与BeginInvoke在主副线程中的执行顺序和区别

今天无意中看到有关Invoke和BeginInvoke的一些资料,不太清楚它们之间的区别.所以花了点时间研究了下. 据msdn中介绍,它们最大的区别就是BeginInvoke属于异步执行的. Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托. Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托. msdn说明: 控件上的大多数方法只能从创建控件的线程调用. 如果已经创建控

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

近日,被Control的Invoke和BeginInvoke搞的头大,就查了些相关的资料,整理如下. Control的Invoke和BeginInvoke 是相对于支线线程(因为一般在支线线程中调用,用来更新主线程ui)Invoke立即插入主线程中执行,而BeginInvoke 要等主线程结束才执行. (一)Control的Invoke和BeginInvoke我们要基于以下认识:(1)Control的Invoke和BeginInvoke与Delegate的Invoke和BeginInvoke是不

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

转自http://www.cppblog.com/baby-fly/archive/2010/04/01/111245.html 一.为什么 Control类提供了 Invoke和 BeginInvoke机制? 关于这个问题的最主要的原因已经是 dotnet程序员众所周知的,我在此费点笔墨再次记录到自己的日志,以便日后提醒一下自己. 1. windows程序消息机制 Windows GUI程序是基于消息机制的,有个主线程维护着一个消息泵.这个消息泵让 windows程序生生不息. Windows

Control的Invoke和BeginInvoke详解

(一)Control的Invoke和BeginInvoke 我们要基于以下认识: (1)Control的Invoke和BeginInvoke与Delegate的Invoke和BeginInvoke是不同的. (2)Control的Invoke和BeginInvoke的参数为delegate,委托的方法是在Control的线程上执行的,也就是我们平时所说的UI线程. 先看一下主线程中调用Invoke和BeginInvoke 我们以代码(一)来看(Control的Invoke): private d

看懂此文,不再困惑于javascript中的事件绑定、事件冒泡、事件捕获和事件执行顺序

最近一个项目基于3维skyline平台,进行javascript二次开发.对skyline事件的设计真是无语至极,不堪折磨啊!抽空学习了下javascript和jquery的事件设计,收获颇大,总结此贴,和大家分享. (一)事件绑定的几种方式 javascript给DOM绑定事件处理函数总的来说有2种方式:在html文档中绑定.在js代码中绑定.下面的方式1.方式2属于在html中绑定事件,方式3.方式4和方式5属于在js代码中绑定事件,其中方法5是最推荐的做法. 方式1: HTML的DOM元素

浅谈PipelineDB系列一: Stream数据是如何写到Continuous View中的

PipelineDB Version:0.9.7 PostgreSQL Version:9.5.3 PipelineDB的数据处理组件: 从上图来看主要就是pipeline_streams,stream_fdw,Continuous View,Transform. 其实就是运用了Postgres的FDW功能来实现的stream功能. 从数据库也能看到这个FDW pipeline=# \des List of foreign servers Name | Owner | Foreign-data

浅谈Xcode5和Xcode7在系统创建的文件夹和文件中的区别

*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } a { color: #4183C4; } a.absent { color: #cc0000; } a.anchor { display: block; padding-left: 30px; margin-left: -30px; cursor: pointer; position: absolute

java面试基础题------》Java 中的父子类静态代码块,代码块,构造方法执行顺序

4.指出下面程序的运行结果. class A { static { System.out.print("1"); } public A() { System.out.print("2"); } } class B extends A{ static { System.out.print("a"); } public B() { System.out.print("b"); } } public class Hello { pu

Java中普通代码块,构造代码块,静态代码块执行顺序

执行顺序:(优先级从高到低.)静态代码块>mian方法>构造代码块>构造方法. 其中静态代码块只执行一次.构造代码块在每次创建对象是都会执行. static块,普通代码块,构造代码块执行顺序 class A{ public A(){ System.out.println("Hello A"); } { System.out.println("block A"); } static{ System.out.println("static A