invoke和begininvoke 区别

一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解。

 首先说下,invoke和begininvoke的使用有两种情况:

  1. control中的invoke、begininvoke。

  2. delegrate中的invoke、begininvoke。  

  这两种情况是不同的,我们这里要讲的是第1种。下面我们在来说下.NET中对invoke和begininvoke的官方定义。

  control.invoke(参数delegate)方法:在拥有此控件的基础窗口句柄的线程上执行指定的委托。

  control.begininvoke(参数delegate)方法:在创建控件的基础句柄所在线程上异步执行指定委托。

  根据这两个概念我们大致理解invoke表是同步、begininvoke表示异步。但是如何来进行同步和异步呢?我们来做一个测试

private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
Thread 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);
}
private void StartMethod()
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
button1.Invoke((EventHandler)(delegate
{
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}));
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
}

论:我们运行后,看下程序的运行顺序,1AAA->2CCC和1BBB->1EEE ->2DDD 。 

解释:主线程运行1AAA,然后1BBB和子线程2CCC同时执行,然后通过invoke来将方法提交给主线程,然后子线 程等待主线程执行,直到主线程将invokemethod方法执行完成(期间必须等待主线程的任务执行完成,才会去执行invoke提交的任务),最后执 行子线程2DDD。

begininvoke 例子:

StringBuilder stb = new StringBuilder();

        private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
stb.Clear();
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
Thread invokeThread = new Thread(new ThreadStart(StartMethod));
invokeThread.Start();
string a = string.Empty;
for (int i = ; i < ; i++) //调整循环次数,看的会更清楚
{
Thread.Sleep();
//Thread.Sleep(200);
a = a + "B";
}
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + a);
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + a);
//textBox1.Text = stb.ToString();
}
private void StartMethod()
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
button1.Invoke((EventHandler)(delegate
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}));
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
} private void button2_Click(object sender, EventArgs e)
{
textBox1.Text = "";
stb.Clear();
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
Thread 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); stb.Append(Thread.CurrentThread.GetHashCode().ToString() + a);
} private void StartMethod2()
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
button1.BeginInvoke((EventHandler)(delegate
{
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}));
//MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
stb.Append(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
} private void button3_Click(object sender, EventArgs e)
{
textBox1.Text = stb.ToString();
}

textbox1 的显示  1AAA3CCC3DDD1BBB1EEE

结论: 我们运行后看看执行的结果:1AAA->3CCC和3DDD->1BBB->1EEE

解释: 主线程运行1AAA,然后1BBB和子线程3CCC同时执行,然后通过begininvoke来将invokemethod方法提交给主线程,然后主线程执行1EEE(主线程自己的任务执行完成), 同时子线程继续执行3DDD。

通过这个两段代码的测试比较,我们会发现其实invoke和begininvoke所提交的委托方法都是在主线程中执行的,其实根据我invoke 和begininvoke的定义我们要在子线程中来看这个问题,在invoke例子中我们会发现invoke所提交的委托方法执行完成后,才能继续执行 DDD;在begininvoke例子中我们会发现begininvoke所提交的委托方法后,子线程继续执行DDD,不需要等待委托方法的完成。 那么现在我们在回想下invoke(同步)和begininvoke(异步)的概念,其实它们所说的意思是相对于子线程而言的,其实对于控件的调用总是由 主线程来执行的。我们很多人搞不清这个同步和异步,主要还是因为我们把参照物选错了。其实有时候光看概念是很容易理解错误的。

解决从不是创建控件的线程访问它

在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界面显示。

正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过 Invoke 或者 BeginInvoke 去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。

而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使 UI 线程的负担不至于太大而已,因为界面的正确更新始终要通过 UI 线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到 UI 线程中去做,这样也就达到了减轻 UI 线程负担的目的了。

举个简单例子说明下使用方法,比如你在启动一个线程,在线程的方法中想更新窗体中的一个TextBox.. 

 

using System.Threading;

//启动一个线程 
Thread thread=new Thread(new ThreadStart(DoWork)); 
thread.Start();

//线程方法 
private void DoWork() 

this.TextBox1.Text="我是一个文本框"; 
}

如果你像上面操作,在VS2005或2008里是会有异常的...

正确的做法是用Invoke\BeginInvoke

 public delegate void MyInvoke(string str1, string str2);
public Form2()
{
InitializeComponent();
} public void DoWork()
{
        
//this.textBox1.Text = "我是文本框 ";   // 错误  提示  线程间操作无效  从不是创建控件textBox1的线程访问它

            MyInvoke mi = new MyInvoke(UpdateForm);
this.BeginInvoke(mi, new Object[] { "我是文本框", "haha" });
} private void button1_Click(object sender, EventArgs e)
{
Thread thr = new Thread(new ThreadStart(DoWork));
thr.Start(); } public void UpdateForm(string param1, string param2)
{
this.textBox1.Text = param1 + param2;
}

注意代理的使用!

 

后面再次补充

在 WinForm开发过程中经常会用到线程,有时候还往往需要在线程中访问线程外的控件,比如:设置textbox的Text属性等等。如果直接设置程序必 定会报出:从不是创建控件的线程访问它,这个异常。通常我们可以采用两种方法来解决。一是通过设置control的属性。二是通过delegate,而通 过delegate也有两种方式,一种是常用的方式,另一种就是匿名方式。下面分别加以说明.

首先,通过设置control的一个属性值为false.我们可以在Form_Load方法中添加:Control.CheckForIllegalCrossThreadCalls=false;来解决。设置为false表示不对错误线程的调用进行捕获。这样在线程中对textbox的Text属性进行设置时就不会再报错了。
其次,通过delegate的方法来解决。
普通的委托方法例如:

delegate void SafeSetText(string strMsg);
private void SetText(string strMsg)
{
if(textbox1.InvokeRequired)
{
SafeSetText objSet=new SafeSetText(SetText);
textbox1.Invoke(objSet,new object[]{strMsg}); }
else
{
textbox1.Text=strMsg;
}
}

在线程内需要设置textbox的值时调用SetText方法既可。我们还可以采用另一种委托的方式来实现,那就是匿名代理,例如:

delegate void SafeSetText(string strMsg);
private void SetText2(string strMsg)
{
  SafeSetText objSet = delegate(string str)
{
textBox1.Text = str;
}
textBox1.Invoke(objSet,new object[]{strMsg});
}

这样同样可以实现。
个人觉得还是采用代理好些。

在C# 3.0及以后的版本中有了Lamda表达式,像上面这种匿名委托有了更简洁的写法。.NET Framework 3.5及以后版本更能用Action封装方法。例如以下写法可以看上去非常简洁:

void ButtonOnClick(object sender,EventArgs e)

{

this.Invoke(new Action(()=>

{

button.Text="关闭";

}));

}

C#中Invoke的用法1的更多相关文章

  1. (转)C#中Invoke的用法 一

    在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...

  2. (@WhiteTaken)Unity中Invoke的用法

    今天无意间读到大神写的代码,看到了Invoke函数,于是产生兴趣.后来才明白自己要学习的东西还有很多. 下面讲用法. Invoke是延时调用函数,在用Invoke函数之前需要引入命名空间using U ...

  3. C#中Invoke的用法2

    在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...

  4. C#中Invoke的用法(转)

    在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界 ...

  5. C#中Invoke的用法()

    invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和beg ...

  6. 【转】C#中Invoke的用法

    在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界 ...

  7. C#中Invoke的用法

    在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫"打开",单击之后按钮上的文本显示 ...

  8. [转载]C#中Invoke的用法()

    invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和beg ...

  9. C#中Invoke的用法()-解决子线程访问主线程控件、线程安全等问题

    引自https://www.cnblogs.com/lsgsanxiao/p/5523282.html invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概 ...

随机推荐

  1. python 全栈开发,Day70(模板自定义标签和过滤器,模板继承 (extend),Django的模型层-ORM简介)

    昨日内容回顾 视图函数: request对象 request.path 请求路径 request.GET GET请求数据 QueryDict {} request.POST POST请求数据 Quer ...

  2. 《剑指offer》-整数中1出现的次数

    题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...

  3. hdu 1757 和1005差不多 (矩阵快速幂)

    If x < 10 f(x) = x.If x >= 10 f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-1 ...

  4. ELK - MAC环境搭建

    ELK - MAC环境搭建 本文旨在记录elasticsearch.logstash.kibana在mac下的安装与启动. 写在前面 ELK的官方文档对与它们的使用方法已经讲的非常清楚了,这里只对相关 ...

  5. 基于kubernetes集群部署DashBoard

    目录贴:Kubernetes学习系列 在之前一篇文章:Centos7部署Kubernetes集群,中已经搭建了基本的K8s集群,本文将在此基础之上继续搭建K8s DashBoard. 1.yaml文件 ...

  6. Android 之 tools:context和tools:ignore两个属性的作用

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools= ...

  7. lnmp thinkphp在linux上支持pathinfo

    在lnmp环境中布置thinkphp 默认不支持pathinfo  的 在nginx.conf文件中的server中更改如下 #include enable-php.conf; #布置下面的支持pat ...

  8. HTTP协议学习笔记(四)

    HTTP协议学习笔记(四) 与 HTTP 协作的 Web 服务器 一台 Web 服务器可搭建多个独立域名的 Web 网站,也可作为通信路径上的中转服务器提升传输效率. 1.用单台虚拟主机实现多个域名 ...

  9. 条件随机场之CRF++源码详解-开篇

    介绍 最近在用条件随机场做切分标注相关的工作,系统学习了下条件随机场模型.能够理解推导过程,但还是比较抽象.因此想研究下模型实现的具体过程,比如:1) 状态特征和转移特征具体是什么以及如何构造 2)前 ...

  10. 我们为什么要学习 Spring Boot

    现在貌似大家都知道 Spring Boot 很火了,做 Java 的不知道 Spring Boot 的都已经 Out 了,但是又有多少人是跟风学习的呢?今天我们就来说一下为什么要学习 Spring B ...