线程实用解析--------(六)Control.Invoke()和Control.BeginInvoke()
在以前的章节中,我们不只一次的提到过,不能在非创建UI控件的线程中操作UI元素,否则会和UI控件创建线程(一般是主线程)产生冲突,造成不可预料的后果。
该如何解决这个问题呢?除了上一节所讲的BackgroundWorker和Timer以外,微软将Control类实现了ISynchronizeInvoke接口,提供了Invoke和BeginInvoke方法来提供让其它线程更新GUI界面控件的机制。
下边还是通过一个例子给大家讲解一下Control.Invoke()和Control.BeginInvoke();
首先新建一个WinForm应用程序,在Form窗体上做如下布局:
然后,新建一个委托
public delegate void ShowTime();
在Control。Invoke()按钮的click事件里添加如下代码:
Button_click(object sender ,EventArgs e)
{
ShowTime showTime = new ShowTime(Time);
this.Invoke(showTime);//Invoke()方法需要一个委托参数,也就是要执行委托方法
//在执行委托之后弹出对话框,显示当前的线程ID,是否为后台线程
MessageBox.Show("当前线程ID为:" + Thread.CurrentThread.ManagedThreadId + "是否为后台线程?" + Thread.CurrentThread.IsBackground);
}
//委托调用的方法
private void Time()
{
this.button10.Text = DateTime.Now.ToString();//将Button.Text属性设置为当前的时间
for (int i = 0; i <= 100; i++)
{
sum += i;
}
MessageBox.Show("Sum值为:" + sum + "当前线程ID为:" + Thread.CurrentThread.ManagedThreadId + "是否为后台线程?" + Thread.CurrentThread.IsBackground);//显示当前的线程ID,是否为后台线程
}
实验结果如下:依次是
1、
2、
3、
由实验结果我们可以得出以下结论:Control.Invoke()方法所执行的委托方法跟创建UI元素的线程是一个线程,也就是主线程。Control.Invoke()虽然执行了委托方法,但是并没有创建新的线程,而且从实验结果出现的先后顺序可以得知,Invoke()是同步独占式的执行,只有在Invoke()方法执行完以后才执行Button-Click()方法里的MessageBox.Show()方法。
下面在看下Control.BeginInvoke()
首先新建一个委托:
public delegate void ShowTime(int a);
然后在Button_click()方法中添加如下代码:
private void button11_Click(object sender, EventArgs e)
{
ShowTime showTime = new ShowTime(Time);//新建一个委托实例
this.BeginInvoke(showTime, 100);//调用Control.BeginInvoke()方法,第一个参数是一个委托实例,第二参数是委托调用函数所需要的参数
MessageBox.Show("当前线程ID为:" + Thread.CurrentThread.ManagedThreadId + "是否为后台线程?" + Thread.CurrentThread.IsBackground);//显示当前的线程ID
}
//委托调用函数
private void Time(int a)
{
this.button11.Text = DateTime.Now.ToString();//改变Button的Text属性为当前的时间
for (int i = 0; i <= a; i++)
{
sum += i;
}
Thread.Sleep(3000);
MessageBox.Show("Sum值为:" + sum + "当前线程ID为:" + Thread.CurrentThread.ManagedThreadId + "是否为后台线程?" + Thread.CurrentThread.IsBackground);//显示当前的线程的ID
}
实验结果如下:依次是:
1、
2、
3、
由实验结果我们可以得出以下结论:Control.BeginInvoke()方法所执行的委托方法跟创建UI元素的线程是一个线程,也就是主线程。Control.BeginInvoke()虽然执行了委托方法,但是并没有创建新的线程,而且从实验结果出现的先后顺序可以得知,BeginInvoke()在这里也是同步执行的,只有在BeginInvoke()方法执行完以后才执行Button-Click()方法里的MessageBox.Show()方法。之前有看网上、园子里说Control.BeginInvoke()是异步执行的这个本没有错。但是所谓的异步在本例中相当于执行this.BeginInvoke(showTime,100);语句之后,应该马上执行下边一句MessageBox.Show();最后再执行委托方法,为了对此进行测试,我在Time()函数里将线程休眠了3秒钟,但是事实证明,只有在Time()方法执行完之后,才会执行MessageBox.show()语句,才会出现上边所示的结果,所谓的异步执行,在这里并没有得到体现,这是为什么呢?让我们继续往下看。
由以上的例子可知Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。如果委托方法不会花费很长的时间,那直接用Control.Invoke()和Control.BeginInvoke()方法,如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死,那么我们该该如何办呢?最好的解决办法就是重新建立一个线程,然后在线程调用函数里再启用Control.Invoke()或者Control.BeginInvoke()
下面还是给大家做一个示例,然后在示例中慢慢讲解。
在Form窗体里放置2个Button如图
先创建一个委托 private delegate void Showing();
然后在Thread调用Control.Invoke()按钮的Click方法中添加如下代码:
private void button4_Click(object sender, EventArgs e)
{
//显示当前线程ID
MessageBox.Show("A,我最先执行!"+Thread.CurrentThread.ManagedThreadId);
//创建一个新的线程,并在线程调用函数ShowThread中,调用Control.Invoke()方法
Thread thread = new Thread(new ThreadStart(ShowThread));
thread.Start();
MessageBox.Show("B,我和C同步执行!" + Thread.CurrentThread.ManagedThreadId);
}
//线程调用方法,在这个方法里再调用this.Invoke()方法操作UI元素
void ShowThread()
{
MessageBox.Show("C,Thread调用Control.Invoke()方法开始!");
this.Invoke(new Showing(ShowResult));
MessageBox.Show("E,我最后执行!,Thread调用Contorol.Invoke()方法结束!");
}
//委托Showing调用的函数
private static void ShowResult()
{
int sum = 0;
for (int i = 0; i < 1000000000; i++)
{
sum += i;
}
MessageBox.Show("D:占用UI线程。----"+Thread.CurrentThread.ManagedThreadId);//显示当前线程所在ID
}
实验结果如下:
依次为1、
2、
3、
4、
由实验结果可知,在线程thread调用的ShowThread方法里,启动了Control.Invoke()方法,在Control.Invoke()方法调用的委托方法ShowResult()完全执行完之后,才执行ShowThread()里的MessageBox.Show("E,我最后执行!,Thread调用Contorol.Invoke()方法结束!");由此可见Control.Invoke()方法是同步的。
下面把Control.BeginInvoke()方法也放在一个新的线程调用函数中执行,代码基本上不用怎么变,只需要把Thread调用Control.Invoke()按钮的Click方法里的代码复制到Thread调用Control.BeginInvoke()按钮的Clicked方法里就行,然后把void ShowThread()的代码做如下改动:
{
MessageBox.Show("C,Thread调用Control.Invoke()方法开始!");
this.BeginInvoke(new Showing(ShowResult));
MessageBox.Show("E,我最后执行!,Thread调用Contorol.Invoke()方法结束!");
}
运行结果如下:1、
2、
3、
4、
由实验结果可知:在线程thread调用的ShowThread方法里,启动了Control.BeginInvoke()方法,Control.BeginInvoke()方法调用的委托方法ShowResult()之后,马上就执行了ShowThread()里的MessageBox.Show("E,我最后执行!,Thread调用Contorol.Invoke()方法结束!"),并没有等待委托方法ShowResult()执行完,由于ShowResult()执行时间较长,故最后才显示消息框4,所以说这里的Control.BeginInvoke()是异步操作。这样就可以与Control.Invoke()是同步操作明显的区分开了。
说到BeginInvoke()和Invoke()还有朋友问Delegate的BeginInvoke()、Invoke()和Contorl.BeginInvoke()、Invoke()的区别,之前的章节中有介绍过Delegate的BeginInvoke(),委托的BeginInvoke()主要用于异步操作,其本质是在线程池里又启用了一个新的后台线程,而Contorl.BeginInvoke()它本身就是在创建控件的线程里(也就是UI)线程里执行的,它主要依附于具体的UI元素,可以直接的访问UI元素,但是通过委托的BeginInvoke()是绝对不能访问UI元素的,这点已经强调了很多次了。
好了,关于《线程实用详解》这一系列的文章到这里已经结束了,通过学习、总结,自己也收获了很多的东西,当然,还有有些地方总结的还不够全面,还需要继续的努力。学习就是这样一个过程,通过不断的练习、反思、总结然后再反思、练习、总结、升华。希望能给大家带来些帮助,也还请大家不吝赐教,共同交流探讨。
线程实用解析--------(六)Control.Invoke()和Control.BeginInvoke()相关推荐
- 千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死...
.c# Invoke和BeginInvoke 区别 Control.Invoke 方法 (Delegate):在拥有此控件的基础窗口句柄的线程上执行指定的委托. Control.BeginInvoke ...
- Control.Invoke和Control.BeginInvoke
问题的引入 下面有个简单的demo,大家一看代码就知道效果如何示例.我新建一个winform的程序,然后写入了如下代码: using System; using System.Windows.Form ...
- WinForm 之Control.Invoke 和Control.BeginInvoke 方法的使用 Control 不能在创建它的 Thread 之外被调用。但可以通过 invoke 来保证 C
WinForm 之Control.Invoke 和Control.BeginInvoke 方法的使用 Control 不能在创建它的 Thread 之外被调用.但可以通过 invoke 来保证 Con ...
- Control.Invoke()和Control.BeginInvoke()
不能在非创建UI控件的线程中操作UI元素,否则会和UI控件创建线程(一般是主线程)产生冲突,造成不可预料的后果. 该如何解决这个问题呢?除了上一节所讲的BackgroundWorker和Timer以外 ...
- 在.Net中进行跨线程的控件操作(上篇:Control.Invoke)
本文的重点在于介绍如何在多线程编程中,从非UI线程上访问界面中的控件.有过多线程编程经验的人都知道,当我们在非UI线程上试图给一个界面中的控件赋值的时候,比如说label的Text属性,系统会抛出一个 ...
- 使用control.invoke返回当前窗口线程[转]
VS2008.C#3.0 在WinForm开发中,我们通常不希望当窗体上点了某个按钮执行某个业务的时候,窗体就被卡死了,直到该业务执行完毕后才缓过来.一个最直接的方法便是使用多线程.多线程编程的方式在 ...
- 浅述WinForm多线程编程与Control.Invoke的应用
在WinForm开发中,我们通常不希望当窗体上点了某个按钮执行某个业务的时候,窗体就被卡死了,直到该业务执行完毕后才缓过来.一个最直接的方法便是使用多线程.多线程编程的方式在WinForm开发中必不可 ...
- Control.Invoke用法注意事项
control.invoke主要用于在非Ui线程更新控件,这里要注意的是,当ui线程阻塞时,在非Ui线程中的control.invoke也会阻塞,control.invoke会一直等到Ui线程有空闲了 ...
- Struts2 官方教程之Struts Tags(六)——Generic Tags(Control Tags )
开头要说的:在早期的应用开发中,表现层Jsp页面主要使用Jsp脚本来控制输出.这样,在Jsp中嵌套了java脚本,这种方式不管是可读性还是可维护性都很差,几乎使Jsp成为Java的子集.从Jsp1.1 ...
最新文章
- spark编程基础--5.1RDD编程基础
- Linux文本三剑客之sed
- 49字母异位词分组(哈希表)
- Python遍历目录的4种方法
- python 文件操作 os.readlines()函数用法
- u3d游戏开发视频潭州_游戏美术行业的发展与应用人工智能学院专业介绍及未来前景系列报告会二...
- python基本符合_python 3-3(2019-11-06 ) Python基础 (三)
- 【Python3网络爬虫开发实战】1.3.3-pyquery的安装
- 微软发布ASP.NET MVC 1.0正式版
- 适合于小团队且周期短的产品迭代的APP测试流程
- UIAutomator2常用类之UiObject2
- JavaScript音频编辑
- Encrypted traffic 加密流量分类任务进展综述
- 【阿里云·云原生架构】白皮书 —— 云原生架构原则
- 通信总线协议五 :CAN
- WebOffice(在线Office编辑)
- arduino 操作stm32
- 打造自己的JavaScript武器库
- LM小型可编程控制器软件(基于CoDeSys)笔记十三:网络资源汇总
- 家庭公网ipv6主机实现流量中转