问题的引入

下面有个简单的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的更多相关文章

  1. 千万别在UI线程上调用Control.Invoke和Control.BeginInvoke,因为这些是依然阻塞UI线程的,造成界面的假死

    原文地址:https://www.cnblogs.com/wangchuang/archive/2013/02/20/2918858.html .c# Invoke和BeginInvoke 区别 Co ...

  2. C#Delegate.Invoke、Delegate.BeginInvoke And Control.Invoke、Control.BeginInvoke

    作者:EasonLeung 一.Delegate的Invoke.BeginInvoke 1.Delegate.Invoke (委托同步调用) a.委托的Invoke方法,在当前线程中执行委托. b.委 ...

  3. C#中的线程二(Cotrol.BeginInvoke和Control.Invoke)

    C#中的线程二(Cotrol.BeginInvoke和Control.Invoke) 原文地址:http://www.cnblogs.com/whssunboy/archive/2007/06/07/ ...

  4. (转)c# control.Invoke control.BeginInvoke

    在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...

  5. WinForm二三事(三)Control.Invoke&Control.BeginInvoke

    http://www.cnblogs.com/yuyijq/archive/2010/01/11/1643802.html 这个系列从2009年写到2010年,差点又成太监文.随着WPF/Silver ...

  6. (转)C#为什么要使用Invoke,它和BeginInvoke有什么区别

    在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...

  7. [C#] Control.Invoke方法和跨线程访问控件(转载)

    转载前,在网上找了好多INVOKE方法的文章,就这个看着还可以,明白了大概,以后再深用的时候再研究 ,废话少说上转载(连转载都说的这么有气势,哈哈)   在设计界面时,我们经常需要将一些需要时间才能完 ...

  8. [C#] Control.Invoke方法和跨线程访问控件

    在设计界面时,我们经常需要将一些需要时间才能完成的操作放在另一个线程(不同于UI主线程)中执行.但是这些操作可能需要将其结果或完成情况通知主线程,比如调用窗体的方法,或者触发事件(由界面响应事件),很 ...

  9. 在.Net中进行跨线程的控件操作(上篇:Control.Invoke)

    本文的重点在于介绍如何在多线程编程中,从非UI线程上访问界面中的控件.有过多线程编程经验的人都知道,当我们在非UI线程上试图给一个界面中的控件赋值的时候,比如说label的Text属性,系统会抛出一个 ...

随机推荐

  1. Android事件机制全然解析

    android事件是一级一级传递的,假设父控件不拦截.就传给子控件,假设父控件想要消费事件也就是拦截事件的话,须要重写这种方法 public boolean onInterceptTouchEvent ...

  2. Android的AsyncTask类的解读

    国庆节放假.搞了半个月都没有上班了,coding的时候一点都不在状态,本来这篇文章是在国庆节前写完的,可是由于自己的懒 惰,导致延期到国庆节,哎,这种习惯真心不好呀...不多说了以下来进入正题 之前我 ...

  3. 在不连接网线的情况下Windos与VM之间如何ping通

    一般情况下,如果宿主主机的网口连接网线并且能够上网,那么按照VM的默认安装,在VM-Settings-Hardware-Network Adapter-Network connection中选择Bri ...

  4. 关于C++ 的eof

    #include<iostream> #include<fstream> #define MAXSIZE 1000 using namespace std; int main( ...

  5. python成长之路——第二天

    cpython:c解释器  .pyc(字节码)——机器码 jpython :java解释器   java字节码   ironpython :C#解释器   C#字节码   .... 上面的:编译完之后 ...

  6. Python函数式编程:内置filter函数使用说明

    filter操作是函数式编程中对集合的重要操作之一,其作用是从原集合中筛选符合条件的条目,组成一个新的集合. 这在我们日常编程中是非常常见的操作.我们通常的做法是通过循环语句来处理. 而使用filte ...

  7. php函数参数

    函数的参数 通过参数列表可以传递信息到函数,即以逗号作为分隔符的表达式列表.参数是从左向右求值的. PHP 支持按值传递参数(默认),通过引用传递参数以及默认参数.也支持可变长度参数列表,更多信息参见 ...

  8. Android常用动画Animation的使用

    Andriod中有几种常用的Animation AlphaAnimation  淡入淡出效果 RotateAnimation 旋转效果 ScaleAnimation 缩放动画 TranslaAnima ...

  9. 一步一步重写 CodeIgniter 框架 (11) —— 使用 CodeIgniter 函数库

    在完成了CI框架的类库扩展后,很自然我们就会想到函数库的扩展.函数库的扩展在 CI 中称为 helper 函数与类有不同的地方,它不能继承,只能覆盖或者添加新的函数,或者直接完全新定义的一组函数. 由 ...

  10. linux定时调度器每秒运行一次

    linux操作系统最小粒度的定时调度器仅仅能调到分钟的级别,工作中有时需在到秒的调度,所以须要自己编写脚本来实现 #!/bin/bash while [ true ]; do /bin/sleep 1 ...