您的位置:首页 > 编程语言 > C#

C#中Invoke与BeginInvoke在线程中的执行解析

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

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

Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
  用下面的代码进行初步测试:

1.主线程调用Invoke   

          /// <summary>

          /// 直接调用Invoke

          /// </summary>

          private void TestInvoke()

          {

              listBox1.Items.Add("--begin--");

              listBox1.Invoke(new Action(() =>

              {

                  listBox1.Items.Add("Invoke");

             }));

 

             Thread.Sleep(1000);

             listBox1.Items.Add("--end--");

         }

 输出:    

  


  从输出结果上可以看出,Invoke被调用后,是马上执行的。这点很好理解。

2.主线程调用BeginInvoke

/// <summary>

          /// 直接调用BeginInvoke

          /// </summary>

          private void TestBeginInvoke()

          {

              listBox1.Items.Add("--begin--");

              var bi = listBox1.BeginInvoke(new Action(() =>

              {

                  //Thread.Sleep(10000);

                listBox1.Items.Add("BeginInvoke");

             }));

             Thread.Sleep(1000);

             listBox1.Items.Add("--end--");

         }

 输出:  

  


  从输出能看出,只有当调用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都会造成阻塞。所以应该利用副线程(支线线程)调用。

 

  [b]3.支线线程调用Invoke[/b]

[b]  [/b]创建一个线程,并在线程中调用Invoke,同时测试程序是在哪个线程中调用Invoke。 

          /// <summary>

          /// </summary>

          private void ThreadInvoke()

          {

              listBox1.Items.Add("--begin--");

              new Thread(() =>

             {

                 Thread.CurrentThread.Name = "ThreadInvoke";

                 listBox1.Invoke(new Action(() =>

                     {

                         Thread.Sleep(10000);

                         this.listBox1.Items.Add("ThreadInvoke:" + Thread.CurrentThread.Name);                  

                     }));

             }).Start();

             Thread.Sleep(1000);

             listBox1.Items.Add("--end--");

         }

 

 

         private void button1_Click(object sender, EventArgs e)

         {

             Thread.CurrentThread.Name = "Main";

             ThreadInvoke();

         }

 

         private void button2_Click(object sender, EventArgs e)

         {

            listBox1.Items.Add("button2_Click");

         }  输出:

  


  当点击button1后,我试图去点击button2,却发现程序被阻塞了。可见Invoke尽管在支线程中调用,实际上仍然在拥有此控件的基础窗口句柄的线程上执行。

   

  接着来测试下在支线程中调用BeginInvoke.

 

  4.支线线程调用BeginInvoke

          /// <summary>

          /// 线程调用BeginInvoke

          /// </summary>

          private void ThreadBeginInvoke()

          {

              listBox1.Items.Add("--begin--");

              new Thread(() =>

              {

                  Thread.CurrentThread.Name = "ThreadBeginInvoke";

                 Thread.Sleep(10000);

                 string temp = "Before!";

                 listBox1.BeginInvoke(new Action(() =>

                 {

                     this.listBox1.Items.Add(temp + ":" + Thread.CurrentThread.Name);

                 }));

                 temp += "After!";

             }).Start();

             Thread.Sleep(1000);

             listBox1.Items.Add("--end--");

         }

 

 

         private void button1_Click(object sender, EventArgs e)

         {

             Thread.CurrentThread.Name = "Main";

             ThreadBeginInvoke();

         }

 

         private void button2_Click(object sender, EventArgs e)

         {

             listBox1.Items.Add("button2_Click");

         }  

输出:  

  


  从这结果中我们能得出以下几点:

线程真正开始执行,是在创建它的线程结束后执行。
真正执行BeginInvoke的线程就是创建控件线程上。
BeginInvoke在线程中调用时,是异步执行的而且没有对主线程造成阻塞。(button2_Click在Before!After!:Main前面)
BeginInvoke只有当创建它的线程结束后执行。(当然你也能通过EndInvoke、Invoke让其提早执行。)
 

总结:  

  以下为了方便理解,假设如下:

    主线程表示Control.Invoke或Control.BeginInvoke中Control所在的线程,即创建该创建的线程。(一般为UI线程)

    支线程表示调用Invoke或BeginInvoke的线程。

Invoke、BeginInvoke始终在主线程中执行。
Invoke被调用时就会直接执行,也就是直接阻塞线程(包括主支线程),直到它结束。而BeginInvoke只有等支线程结束或者调用EndInvoke、Invoke时才会开始执行。
Invoke不管在哪里执行都会造成主线程的阻塞。而BeginInvoke只会阻塞支线程,而对于主线程是异步执行。(注意,如果在主线程中调用,也会阻塞主线程)。
在支线程中,应该使用BeginInvoke,否则调用Invoke将导致支线程阻塞主线程,该支线程就没有存在的意义。(当然有特殊需求除外[b])。[/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐