Control.Invoke和Control.BeginInvoke
问题的引入
下面有个简单的demo,大家一看代码就知道效果如何示例。我新建一个winform的程序,然后写入了如下代码:
using System;
using System.Windows.Forms; namespace MyExampleList
{
public partial class ControlInvokeExample : Form
{
public ControlInvokeExample()
{
InitializeComponent();
} private void btnSayHello_Click(object sender, EventArgs e)
{
txtHello.Text = "祝你学习C#愉快";
}
}
}
运行效果如下:
[caption id="attachment_1150" align="alignnone" width="364"] 显示文本[/caption]
下面我来改造一下这个简单的demo。我们在编程的时候使用多线程是非常的常见,我们在点击按钮的时候启用另一个线程,然后来操作textbox的值,让它进行变化,代码如下:
using System;
using System.Threading;
using System.Windows.Forms; namespace MyExampleList
{
public partial class ControlInvokeExample : Form
{
public ControlInvokeExample()
{
InitializeComponent();
} private void btnSayHello_Click(object sender, EventArgs e)
{
Thread th=new Thread(new ThreadStart(StartMethord));
th.Start(); } private void StartMethord()
{
txtHello.Text = "祝你学习C#愉快";
}
}
}
理论上就应该是上面的代码就可以执行的,但是实际上会报错,具体错误如下图所示:
[caption id="attachment_1152" align="alignnone" width="860"] 线程交互错误[/caption]
错误解释:上面的错误意思就是txtHello这个控件不是th创建的所以th线程不能直接访问它,因此报错了。
那我们如何让th线程也能改变UI中的txtHello这个textbox的值呢?
我们通过Control.Invoke或者Control.BeginInvoke来实现。
Control.Invoke和Control.BeginInvoke
Control.Invoke:在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Control.BeginInvoke:在创建控件的基础句柄所在线程上异步执行指定委托。
根据上面的解释我们来修改一下上面出错的代码,用Invoke的方式来实现th线程更改txtHello的内容,具体代码如下(我称下面的这种实现方式为:A):
using System;
using System.Threading;
using System.Windows.Forms; namespace MyExampleList
{
public partial class ControlInvokeExample : Form
{
public ControlInvokeExample()
{
InitializeComponent();
}
delegate void InvokeDelegate();
private void btnSayHello_Click(object sender, EventArgs e)
{
Thread th=new Thread(new ThreadStart(StartMethord));
th.Start();
}
private void StartMethord()
{
txtHello.Invoke(new InvokeDelegate(ChangeTextBox));
}
private void ChangeTextBox()
{
txtHello.Text = "祝你学习C#愉快";
}
}
}
用BeginInvoke的方式来实现th线程更改txtHello的内容,具体代码如下(我称下面的这种实现方式为:B):
using System;
using System.Threading;
using System.Windows.Forms; namespace MyExampleList
{
public partial class ControlInvokeExample : Form
{
public ControlInvokeExample()
{
InitializeComponent();
}
delegate void InvokeDelegate();
private void btnSayHello_Click(object sender, EventArgs e)
{
Thread th=new Thread(new ThreadStart(StartMethord));
th.Start();
}
private void StartMethord()
{
txtHello.BeginInvoke(new InvokeDelegate(ChangeTextBox));
}
private void ChangeTextBox()
{
txtHello.Text = "祝你学习C#愉快";
}
}
}
我们对比一下A和B两种实现方式,在代码上的区别,唯一的区别就是A使用了txtHello.Invoke和B方式用了txtHello.BeginInvoke,也就是他们的区别又归到了Invoke和BeginInvoke上,一个是同步,一个是异步,也就是A方式的时候是等待Invoke的执行结果,而B方式的时候是直接执行下去不等待BeginInvoke的执行结果。在本例的demo中方式A和B实现的效果相同,但是原理不同,仅此而已。下面我来重新写一个新的demo,让大家见识一下这两个方法的区别。
修改后的实例代码如下:
using System;
using System.Threading;
using System.Windows.Forms; namespace MyExampleList
{
public partial class ControlInvokeExample : Form
{
public ControlInvokeExample()
{
InitializeComponent();
}
delegate void InvokeDelegate();
private void btnSayHello_Click(object sender, EventArgs e)
{
Thread th=new Thread(new ThreadStart(StartMethord));
th.Start(); }
private void StartMethord()
{
DateTime dt1 = DateTime.Now;
txtHello.Invoke(new InvokeDelegate(ChangeTextBox));//等待ChangeTextBox方法执行完毕,所以str的值比较大
// txtHello.BeginInvoke(new InvokeDelegate(ChangeTextBox));//不等待ChangeTextBox方法执行完毕,所以str的值会很小
string str= (DateTime.Now - dt1).TotalMilliseconds.ToString(); }
private void ChangeTextBox()
{
Thread.Sleep(3000);
txtHello.Text = "祝你学习C#愉快"+DateTime.Now.ToString();
}
}
}
运行后的结果如下两图:
[caption id="attachment_1154" align="alignnone" width="630"] invoke结果[/caption]
[caption id="attachment_1155" align="alignnone" width="584"] BeginInvoke结果[/caption]
差距很明显了吧。
那么什么时候我们必须使用Invoke和BeginInvoke呢?
InvokeRequired:获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程(本例中的UI线程)以外的线程(自定义线程th)中。
也就是当InvokeRequired为true时我们必须用Invoke或者BeginInvoke方法来更新界面。
特别注意
Control的Invoke和BeginInvoke的委托方法是在主线程,即UI线程上执行的。也就是说如果你的委托方法用来取花费时间长的数据,然后更新界面什么的,千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死。
那么,这个异步到底是什么意思呢?(具体的解释可以看上面的A和B方式,指的是th线程的异步)
异步是指相对于调用BeginInvoke的线程异步,而不是相对于UI线程异步,你在UI线程上调用BeginInvoke ,当然不行了。----摘自"Invoke和BeginInvoke的真正涵义"一文中的评论。
BeginInvoke的原理是将调用的方法Marshal成消息,然后调用Win32 API中的RegisterWindowMessage()向UI窗口发送消息。----摘自"Invoke和BeginInvoke的真正涵义"一文中的评论。
Control.Invoke和Control.BeginInvoke的更多相关文章
- 千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死
原文地址:https://www.cnblogs.com/wangchuang/archive/2013/02/20/2918858.html .c# Invoke和BeginInvoke 区别 Co ...
- C#Delegate.Invoke、Delegate.BeginInvoke And Control.Invoke、Control.BeginInvoke
作者:EasonLeung 一.Delegate的Invoke.BeginInvoke 1.Delegate.Invoke (委托同步调用) a.委托的Invoke方法,在当前线程中执行委托. b.委 ...
- C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)
C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...
- (转)c# control.Invoke control.BeginInvoke
在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...
- WinForm二三事(三)Control.Invoke&Control.BeginInvoke
http://www.cnblogs.com/yuyijq/archive/2010/01/11/1643802.html 这个系列从2009年写到2010年,差点又成太监文.随着WPF/Silver ...
- (转)C#为什么要使用Invoke,它和BeginInvoke有什么区别
在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...
- [C#] Control.Invoke方法和跨线程访问控件(转载)
转载前,在网上找了好多INVOKE方法的文章,就这个看着还可以,明白了大概,以后再深用的时候再研究 ,废话少说上转载(连转载都说的这么有气势,哈哈) 在设计界面时,我们经常需要将一些需要时间才能完 ...
- [C#] Control.Invoke方法和跨线程访问控件
在设计界面时,我们经常需要将一些需要时间才能完成的操作放在另一个线程(不同于UI主线程)中执行.但是这些操作可能需要将其结果或完成情况通知主线程,比如调用窗体的方法,或者触发事件(由界面响应事件),很 ...
- 在.Net中进行跨线程的控件操作(上篇:Control.Invoke)
本文的重点在于介绍如何在多线程编程中,从非UI线程上访问界面中的控件.有过多线程编程经验的人都知道,当我们在非UI线程上试图给一个界面中的控件赋值的时候,比如说label的Text属性,系统会抛出一个 ...
随机推荐
- C# 调用迅雷 7 迅雷下载开放引擎
最近有个项目需要用winform调用迅雷下载.上网百度后发现迅雷自带的com组件从迅雷5之后就废掉了,除了能添加任务,其余功能全不能用.后来又发现了迅雷下载开放引擎这么个东西 http://thund ...
- 【甘道夫】使用HIVE SQL实现推荐系统数据补全
需求 在推荐系统场景中,假设基础行为数据太少,或者过于稀疏,通过推荐算法计算得出的推荐结果非常可能达不到要求的数量. 比方,希望针对每一个item或user推荐20个item,可是通过计算仅仅得到8个 ...
- 【OpenCV】OpenCV2.4.6 与Visiual Studio 2008,Python2.7.5配置和图像载入显示
自从OpenCV2.2开始,OpenCV 库便分成几个模块并位于lib文件中,本节介绍从OpenCV2.4.6与VS2008 .Python2.7.5如何配置环境,如何外部文件载 入图像.在窗口中显示 ...
- ZooKeeper - Perl bindings for Apache ZooKeeper Perl绑定用于 Apache ZooKeeper
ZooKeeper - Perl bindings for Apache ZooKeeper Perl绑定用于 Apache ZooKeeper 监控 master/slave 需要使用zk的临时节点 ...
- HDU SPFA算法 Invitation Cards
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1535 分析: 题意:求1点到其它点的最短距离之和+其它点到1点的最短距离之和 前面一部分直接用SPFA ...
- 使用gson(一)
1.数组和json的转换 package com.test.gson; import com.google.gson.Gson; public class ArrayToJson { public s ...
- Android模拟器的文件目录介绍
文件存放在 .avd文件夹下 .ini为对应的配置文件 打开.avd文件夹 *.lock文件夹保存的是模拟器的一下数据,当模拟器正常关闭时这些文件夹都会被自动删除. 当模拟器无法开启的时候可以 ...
- visual studio 2010配置驱动开发环境
visual studio 2010 配置驱动开发环境 ** 工具/材料 VS2010.WDK开发包 ** 配置过程 以下将讲述VS2010驱动开发环境的配置过程,至于必要软件的安装过程这里不再赘述 ...
- Qt持久性对象进行序列化(同时比较了MFC与Java的方法)
Mfc和Java中自定义类的对象都可以对其进行持久性保存,Qt持久性对象进行序列化当然也是必不可少的.不过这个问题还真困扰了我很长时间……Mfc通过重写虚函数Serialize().Java则是所属的 ...
- Android_简单笔记一
入门学习Android的简单笔记(已经安装好了开发环境ADT) 一.关于 AndroidManifest.xml文件 1. android:icon和android:label定义了应用程序安装后显示 ...