关于控件的Invoke(...)方法和BeginInvoke(...)方法的区别

这两个方法最主要的区别就是一个是同步,一个是异步,即会阻塞线程,那么阻塞哪个线程呢?我们用代码来分析(工具是VS2010)

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Data;
 5 using System.Drawing;
 6 using System.Linq;
 7 using System.Text;
 8 using System.Windows.Forms;
 9 using System.Threading;
10 
11 namespace Invoke和BeginInvoke
12 {
13     public partial class Form1 : Form
14     {
15         public Form1()
16         {
17             InitializeComponent();
18         }
19 
20         private void button1_Click(object sender, EventArgs e)
21         {
22             // HH 是24小时制,hh是12小时制
23             MessageBox.Show(Thread.CurrentThread.GetHashCode()+" AAA "+DateTime.Now.ToString(" HH.mm.ss.fffff"));
24             Thread thd = new Thread(ThreadMethod);
25             thd.IsBackground = true;  // 设置为后台线程,该线程即使没有执行完也会随着主线程退出而退出。
26             thd.Start();
27             Thread.Sleep(3000);
28             MessageBox.Show(Thread.CurrentThread.GetHashCode() + " EEE " + DateTime.Now.ToString(" HH.mm.ss.fffff "));
29         }
30 
31         private void ThreadMethod()
32         {
33             MessageBox.Show(Thread.CurrentThread.GetHashCode() + " BBB " + DateTime.Now.ToString("HH.mm.ss.fffff"));
34             // Invoke会阻塞 ThreadMethod() 所在的线程(子线程)
35             label1.Invoke(new Action(InvokeMethod));  // Flag
36             MessageBox.Show(Thread.CurrentThread.GetHashCode() + " DDD " + DateTime.Now.ToString("HH.mm.ss.fffff"));
37         }
38 
39         private void InvokeMethod()
40         {
41             MessageBox.Show(Thread.CurrentThread.GetHashCode() + " CCC " + DateTime.Now.ToString("HH.mm.ss.fffff"));
42         }
43     }
44 }

将上面代码运行后(我这个是个窗体,有个label1和button1,且button1有Click事件),首先是MessageBox.Show(...)会阻塞主线程,如果不按掉AAA的那个显示框不会继续执行代码。

按掉AAA后立刻出现BBB,然后有两种情况,很重要:

①等待3000毫秒的样子出现EEE框,这时候再按掉BBB,这时候会执行InvokeMethod(),该函数是由创建label1的线程来执行。这时候弹出CCC,因为Invoke(...)本身会阻塞子线程ThreadMethod,故没有点掉CCC是不会出现DDD的

②点掉AAA后立刻点掉BBB,此时执行ThreadMethod中的label1.Invoke(...),这句代码拥有“两种阻塞”,第一种Invoke本身就是同步函数,故它会阻塞线程ThreadMethod,使得label1.Invoke(...)执行完之前(而它是否执行完又取决于线程InvokeMethod)不会弹出DDD来(即没有按掉CCC前不会弹出DDD)。第二种阻塞就是label1.Invoke是一个很特别的函数,他会使得系统让主线程(因为是主线程创建的label1)来执行label1.Invoke(...)中的方法,即InvokeMethod,而此时主线程正在Sleep(3000),故这段时间内将不会弹出CCC的提示框,等主线程Sleep(3000)完毕后首先弹出的是CCC而不是EEE(即label1.Invoke(...)会将InvokeMethod()放在其所在线程优先执行),

且情况②如果不点掉CCC不会出现DDD和EEE(这里要注意,尽管不点掉CCC会同时阻塞主线程和子线程,但是阻塞子线程是因为label1.Invoke(...)是同步的,没有执行玩InvokeMethod() Invoke()也不会结束;而阻塞主线程的不是

Invoke(),而是因为InvokeMethod()是在主线程中执行的,MessageBox.Show(...."CCC"...)阻塞了主线程,与label1.Invoke(...)无关),点掉CCC后,立刻“同时”执行DDD和EEE。

将Invoke换成BeginInvoke(...)后,同样②情况,上面的阻塞情况不会发生,即 即使CCC没有点掉也会接着执行DDD(因为label1.BeginInvoke(...)不会阻塞ThreadMethod(),但是注意!!!EEE在CCC没有点掉之前是不会执行的(且仍要等主线程Sleep(3000)后才会弹出CCC提示框),因为阻塞EEE的是InvokeMethod()中的MessageBox.Show(.CCC..)。

原文地址:https://www.cnblogs.com/silentdoer/p/4997700.html