[转载]C#中Invoke的用法()
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表示异步。但是如何来进行同步和异步呢?我们来做一个测试。
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开发过程中经常会用到线程,有时候还往往需要在线程中访问线程外的控件,比如:设置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的用法()的更多相关文章
- [转载]PyTorch中permute的用法
[转载]PyTorch中permute的用法 来源:https://blog.csdn.net/york1996/article/details/81876886 permute(dims) 将ten ...
- (转)C#中Invoke的用法 一
在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...
- [转载]C#中MessageBox.Show用法以及VB.NET中MsgBox用法
一.C#中MessageBox.Show用法 MessageBox.Show (String) 显示具有指定文本的消息框. 由 .NET Compact Framework 支持. MessageBo ...
- [转载]js中return的用法
一.返回控制与函数结果,语法为:return 表达式; 语句结束函数执行,返回调用函数,而且把表达式的值作为函数的结果 二.返回控制,无函数结果,语法为:return; 在大多数情况下,为事件处理函 ...
- [转载]Tensorflow中reduction_indices 的用法
Tensorflow中reduction_indices 的用法 默认时None 压缩成一维
- (@WhiteTaken)Unity中Invoke的用法
今天无意间读到大神写的代码,看到了Invoke函数,于是产生兴趣.后来才明白自己要学习的东西还有很多. 下面讲用法. Invoke是延时调用函数,在用Invoke函数之前需要引入命名空间using U ...
- C#中Invoke的用法2
在用.NET Framework框架的WinForm构建GUI程序界面时,如果要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击之后按钮上的文本显示“关闭”,初学者往往 ...
- C#中Invoke的用法(转)
在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,Invoke 和 BeginInvoke 就是为了解决这个问题而出现的,使你在多线程中安全的更新界 ...
- C#中Invoke的用法()
invoke和begininvoke 区别 一直对invoke和begininvoke的使用和概念比较混乱,这两天看了些资料,对这两个的用法和原理有了些新的认识和理解. 首先说下,invoke和beg ...
随机推荐
- Sonatype Nexus Repository Manager修改密码不成功
nexus修改用户密码时出现Invalid authentication ticket 搜索一下,说会修改密码操作要在15秒内完成 ,于是快速操作,没想到真成功了
- Mac本如何卸载MySQL
Mac本如何卸载MySQL 在Mac上卸载MySQL上一件非常麻烦的事,如果没有卸载干净,就会无法安装新的MySQL 怎样才能完全卸载MySQL呢?(包括所有数据库) 执行以下操作: #打开终端 ...
- PBFT(拜占庭容错)简述
共识算法 区块链中最重要的便是共识算法,比特币使用的是POW(Proof of Work,工作量证明),以太币使用的是POS(Proof of Stake,股权证明)使得算力变的不怎么重要了,而今PO ...
- Centos7搭建Postfix发送邮件 Connection timed out
telent mx1.qq.com 25 25这个端口是不加密的,不安全,qq邮箱和网易的邮箱早就不用了.采用加密的方式
- golang编程之我见
golang编程之我见 学习了两个月的golang,语法算是基本掌握了,从一个C++程序员的角度,提出自己的几个看法吧. 1,没有一个好的包管理工具. 我在公司用的是glide的包管理,这个工具的好处 ...
- tensorflow mac安装方法
480 pip install https://storage.googleapis.com/tensorflow/mac/tensorflow-0.5.0-py2-none-any.whl 481 ...
- Hybrid设计--账号体系的建设
前后端分离:开发效率高,没有SEO 现在是重客户端设计:交互和业务逻辑是前端来写,适合做前后端分离.对前端更友好,提高了效率. 传统模式开发:整个业务逻辑是server端写,不适合做前后端分离.ser ...
- 利用yum升级Centos6的gcc版本,使其支持C++11
下面的可以在centos6下工作,centos7下有问题.可能是因为centos下的scl我是拷贝的文件,没有完全验证centos6下肯定没问题. https://my.oschina.net/u/5 ...
- lldb使用
常用 si,ni ,ex, mem,di,reg,b,c,x 以下内容是lldb帮助文档中内容: apropos -- List debugger commands related ...
- Pycharm进行版本管理
即然pycharm为python提供了这么强大的IDE,那么,我们代码管理,没理由不用版本管理工具Git,SVN等等 在pychram中使用GitHub进行代码管理;需要准备: 1)GitHub帐号: ...