C#:invoke 与 BeginInvoke使用区别

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表示异步

如果你的后台线程在更新一个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\BeginInvokeusing 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="关闭";}));}//最新:Invoke(() =>
{button.Text="关闭";
});

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/252087.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django基本命令

Django基本命令 1.创建一个Django 项目 django_admin.py startproject mysite当前目录下会生成mysite的工程&#xff0c;目录结构如下&#xff1a; manage.py ----- Django项目里面的工具&#xff0c;通过它可以调用django shell和数据库等。settings.py ---- 包含了项目的默认…

Git忽略规则.gitignore梳理

对于经常使用Git的朋友来说&#xff0c;.gitignore配置一定不会陌生。废话不说多了&#xff0c;接下来就来说说这个.gitignore的使用。首先要强调一点&#xff0c;这个文件的完整文件名就是".gitignore"&#xff0c;注意最前面有个“.”。 一般来说每个Git项目中都需…

第二周CoreIDRAW课总结

1.这节课学到了什么知识&#xff1f; 学到了图像的复制&#xff0c;再制鼠标复制&#xff0c;重复&#xff0c;还有对象的对齐&#xff0c;对象的分布顺序。 2.有哪些心得体会&#xff1f; 做了课本的练习&#xff0c;会用窗口里面的泊坞窗造型命令也作出了一个作品。 3.这节课…

axios关于针对请求时长策略设计的思考

前言 在我们的业务请求中&#xff0c;有很多时候会针对有不同时长的需求策略性设置。这里针对这个需求进行详细的展开。 针对这种情况&#xff0c;我们的timout的一般是根据请求地址来的&#xff0c;所以核心处理技巧便是如何根据不同的request地址去设置不同的timeout. 我们之…

C#:WinForm无边框窗体移动方法、模仿鼠标单击标题栏移动窗体位置

方法一&#xff1a;直接通过修改窗体位置从而达到移动窗体的效果 方法二&#xff1a;直接伪装发送单击任务栏消息&#xff0c;让应用程序误以为单击任务栏从而移动窗体 方法一 1.定义一个位置信息Point用于存储鼠标位置 private Point mPoint; 2.给窗体等控件增加MouseDown…

Python 字典删除元素clear、pop、popitem

同其它python内建数据类型一样&#xff0c;字典dict也是有一些实用的操作方法。这里我们要说的是字典删除方法&#xff1a;clear()、pop()和popitem()&#xff0c;这三种方法的作用不同&#xff0c;操作方法及返回值都不相同。接下来就来查看下这些字典特定方法的具体用法是什么…

reactor模式:多线程的reactor模式

上文说到单线程的reactor模式 reactor模式&#xff1a;单线程的reactor模式 单线程的reactor模式并没有解决IO和CPU处理速度不匹配问题&#xff0c;所以多线程的reactor模式引入线程池的概念&#xff0c;把耗时的IO操作交由线程池处理&#xff0c;处理完了之后再同步到selecti…

Elasticsearch实战篇——Spring Boot整合ElasticSearch

2019独角兽企业重金招聘Python工程师标准>>> 当前Spring Boot很是流行&#xff0c;包括我自己&#xff0c;也是在用Spring Boot集成其他框架进行项目开发&#xff0c;所以这一节&#xff0c;我们一起来探讨Spring Boot整合ElasticSearch的问题。 本文主要讲以下内容…

C#:Dockpanel的一些入门的基本操作

原文链接&#xff1a; 一、引用&#xff1a; 1.建立一个WinForm工程&#xff0c;默认生成了一个WinForm窗体Form1&#xff08;此处默认为主窗体&#xff09;。 2.引用—>添加引用—>浏览—>weiFenLuo.winFormsUI.Docking.dll。 3.设置Form1窗体属性IsMdiContainer…

MyBatis中if,where,set标签

<if>标签 <select id"findActiveBlogWithTitleLike"resultType"Blog">SELECT * FROM BLOG WHERE state ‘ACTIVE’ <if test"title ! null">AND title like #{title}</if> </select> if标签通常伴随着where,set…

Python3基础 __repr__ 类的实例对象的名字 可以打印文字(1)

引用自&#xff1a;http://www.bubuko.com/infodetail-1918622.html 这个__repr__的作用从下边的例子中可以看出,返回实例化对象的表达 code: class MyClass() :def __str__(self) :return "我是MyClass的一个实例"def __repr__(self) :return "这回连print都省…

Day03:文件打开;错误处理

错误处理 try: #要执行的代码 except 错误的类型&#xff08;可选&#xff09;: #发生错误时执行的代码 finally: #有没有发生错误都执行的代码 复制代码with open() as 变量名&#xff1a; with提供一种叫上下文管理协议的python技术&#xff0c;系统会自动关闭文件 open() 默…

Python: pip升级报错了:You are using pip version 10.0.1, however version 20.3.3 is available.

1,Python使用命令&#xff1a;python -m pip install --upgrade pip升级pip的时候报了下面这个错 2,换了个命令&#xff1a; python -m pip install --upgrade pip -i https://pypi.douban.com/simple 更新成功了&#xff0c;但又报了一个新的错误&#xff1a; AttributeError:…

新手上路之Hibernate:第一个Hibernate例子

一、Hibernate概述 &#xff08;一&#xff09;什么是Hibernate&#xff1f; Hibernate核心内容是ORM&#xff08;关系对象模型&#xff09;。可以将对象自动的生成数据库中的信息&#xff0c;使得开发更加的面向对象。这样作为程序员就可以使用面向对象的思想来操作数据库&…

模板标签及模板的继承与引用

1.常用的模板标签 - 作用是什么:提供各种逻辑 view.py: def index(request):#模板标签 --常用标签 总结&#xff1a;语法 {% tag %} {% endtag %} {% tag 参数 参数 %} 示例 展示页index.html&#xff0c;包含for标签&#xff0c;if标签&#xff0c;url标签 {% extends teacher…

文件夹操作之创建

创建文件夹可通过Directory类的CreateDirectory方法来实现格式为&#xff1a;Directory.CreateDirectory(“文件路径”)&#xff1b;String path“C:\Users\Administrator\Desktop\51zxw”&#xff1b; If&#xff08;Directory.exists&#xff08;path&#xff09;&#xff09…

doxygen

http://www.doxygen.nl/转载于:https://www.cnblogs.com/zengkefu/p/7383793.html

C#:RichTextBox 追加其它颜色的行列

1、新建静态扩展方法public static class RichTextBoxExtension{public static void AppendTextColorful(this RichTextBox rtBox, string text, Color color, bool addNewLine true){if (addNewLine){text Environment.NewLine;}rtBox.SelectionStart rtBox.TextLength;rtB…

Golang实现一个密码生成器

小地鼠防止有人偷他的果实&#xff0c;在家里上了一把锁。这个锁怎么来的呢&#xff1f;请往下看。。 package mainimport ("flag""fmt""math/rand""time" )var (length intcharset string )const (NUmStr "0123456789"C…

Java基础知识(二)

1、String、StringBuffer、StringBuilder 操作少量数据->String单线程操作字符串缓冲区下操作大量数据->StringBuilder多线程操作字符串缓冲区下操作大量数据->StringBuffer可变性&#xff1a;String类中使用final关键字private final char value[]&#xff0c;所以St…