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

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

  1. control中的invoke、begininvoke。

  2. delegrate中的invoke、begininvoke。

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

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

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

根据这两个概念我们大致理解invoke表是同步、begininvoke表示异步。

如果你的后台线程在更新一个UI控件的状态后不需要等待,而是要继续往下处理,那么你就应该使用BeginInvoke来进行异步处理。

如果你的后台线程需要操作UI控件,并且需要等到该操作执行完毕才能继续执行,那么你就应该使用Invoke。

我们来做一个测试。

invoke 例子:

private void button1_Click(object sender, EventArgs e)
{MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"AAA");invokeThread = new Thread(new ThreadStart(StartMethod));invokeThread.Start();string a = string.Empty;for (int i = 0; i < 3; i++)   //调整循环次数,看的会更清楚{Thread.Sleep(1000);a = a + "B";}MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+a);
}private void StartMethod()
{MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"CCC");button1.Invoke(new invokeDelegate(invokeMethod));MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"DDD");
}private void invokeMethod()
{//Thread.Sleep(3000);MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}

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

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

begininvoke 例子:

private void button1_Click(object sender, EventArgs e)
{MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"AAA");invokeThread = new Thread(new ThreadStart(StartMethod));invokeThread.Start();string a = string.Empty;for (int i = 0; i < 3; i++)   //调整循环次数,看的会更清楚{Thread.Sleep(1000);a = a + "B";}MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+a);
}private void StartMethod()
{MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"CCC");button1.BeginInvoke(new invokeDelegate(invokeMethod));MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString()+"DDD");
}private void beginInvokeMethod(){//Thread.Sleep(3000);MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEEEEEEEEEEE");}

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

解释: 主线程运行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

using System.Threading;
namespace test
{public partial class Form1 : Form{public delegate void MyInvoke(string str1,string str2);public Form1(){InitializeComponent();}public void DoWork(){MyInvoke mi = new MyInvoke(UpdateForm);this.BeginInvoke(mi, new Object[] {"我是文本框","haha"});}public void UpdateForm(string param1,string parm2){this.textBox1.Text = param1+parm2;}private void button1_Click(object sender, EventArgs e){Thread thread = new Thread(new ThreadStart(DoWork));thread.Start();}}
}

注意代理的使用!

后面再次补充
  在 WinForm开发过程中经常会用到线程,有时候还往往需要c#教程在线程中访问线程外的控件,比如:设置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方法既python基础教程可。我们还可以采用另一种委托的方式来实现,那就是匿名代理,例如:

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="关闭";}));
}

最新:

Invoke(() =>
{button.Text="关闭";
});

C# Invoke,begininvoke的用法详解相关推荐

  1. FileSystemWatcher用法详解

    FileSystemWatcher用法详解 FileSystemWatcher控件主要功能: 监控指定文件或目录的文件的创建.删除.改动.重命名等活动.可以动态地定义需要监控的文件类型及文件属性改动的 ...

  2. C# FileSystemWatcher用法详解

    C# FileSystemWatcher用法详解 夜之子 Path指定监控目录, 读取时不能以占用的方式读取 FileSystemWatcher控件主要功能: 监控指定文件或目录的文件的创建.删除.改 ...

  3. python argv 详解_Python3 sys.argv[ ]用法详解

    sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个"外部"很关键,因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可 ...

  4. oracle中的exists 和 not exists 用法详解

    from:http://blog.sina.com.cn/s/blog_601d1ce30100cyrb.html oracle中的exists 和 not exists 用法详解 (2009-05- ...

  5. ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)

    ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多) https://blog.csdn.net/qq_25221835/article/details/82762416 post ...

  6. python的继承用法_【后端开发】python中继承有什么用法?python继承的用法详解

    本篇文章给大家带来的内容是关于python中继承有什么用法?python继承的用法详解,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 面向对象三大特征 1.封装:根据职责将属性和方法 ...

  7. C++中substr()函数用法详解

    C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...

  8. php theme_path,PHP_Yii2主题(Theme)用法详解,本文实例讲述了Yii2主题(Theme) - phpStudy

    Yii2主题(Theme)用法详解 本文实例讲述了Yii2主题(Theme)用法.分享给大家供大家参考,具体如下: 首先看看主要的配置方式: 'components' => [ 'view' = ...

  9. LayoutInflater的inflate函数用法详解

    LayoutInflater的inflate函数用法详解 LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 获取LayoutInflater的方法有如下三种: ...

最新文章

  1. php 空间类元素引入_引入单元素模式
  2. 在FreeBSD下安装mysql+apache+php
  3. Windows phone 8 学习笔记(5) 图块与通知
  4. 解决torch.cuda.is_available()为False的问题
  5. E/Trace: error opening trace file: No such file or directory
  6. “我的开源项目被科技巨头拿去做产品了,注释中连名字都被删掉了”
  7. Oracle.start with … connect by [… and] prior…order siblings by …
  8. 关于u8, u16, u32, u64
  9. sitemap死链检测工具-免费sitemap死链检测抓取工具
  10. O2OA开源企业办公开发平台:使用Vue-CLI开发O2应用
  11. CCNA题库第一部分
  12. golang html转成pdf,使用_go-wkhtmltopdf_将html转pdf
  13. 找不到Office安装路径,如何导入MathType
  14. Automader 使用教程 - 01 你好,左右抽
  15. word在试图打开文件时遇到错误,一种解决办法
  16. SpringMVC 上传文件或图片并以二进制流的方式存到数据库
  17. php判断字符串长度
  18. 企业精益生产之成本管理控制的四大要点
  19. 用flask开发个人博客(37)—— 使用Flask-pagedown实现博客文章预览的功能
  20. 区块链在车联网数据共享领域的研究进展

热门文章

  1. 【python学习】-matplotlib绘图如何将坐标轴刻度值设置为科学计数法形式
  2. 彭八百大师馆藏珍品IP系列数字藏品火爆发售
  3. 生日悖论分析python_Python关于生日悖论分析
  4. UE4轮廓描边【非后处理】
  5. 微信的QQ邮箱助手不提醒,怎么解决?
  6. 删除 Windows 的默认打开方式
  7. 抖音企业号有什么好处?
  8. UniAPP-Android原生插件开发与打包
  9. 怎样将AutoCAD图转换成jpg等图片格式
  10. nlp-Embedding