http://msdn.microsoft.com/zh-cn/library/system.windows.forms.control.invokerequired(v=VS.90).aspx

在.net中,控件的访问更新只能在"拥有"这个控件的线程上执行,否则回抛异常。

MS在Control类上提供了一个InvokeRequired的属性。下面是MSDN对这个属性的一个注释。我这里只有中文版的。呜呜。

Control.InvokeRequired 属性

获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。

我查过英文版的注释,无论是中文的还是英文的,都强调了一点,就是说控件只能在创建此控件的线程上才能被调用。但是实际上却不是这样子的。

正确地说,这里有两个概念,即创建控件的线程和拥有控件的线程。关于"拥有"这个词,这里只是我自己YY出来的,但愿能说明问题,后面还有更多的解释。

创建控件的线程很好理解,那什么是拥有控件的线程呢。两者是一样的吗?不一样。

拥有控件的线程。我这里指的应该是创建了此控件handle的线程,而不是创建控件本身的那个线程。两者可以是相同的,也可以是不同的。

那线程怎么样创建一个控件的handle呢?很简单,我们还是先看看MSDN对Handle的说明。

Control.Handle 属性

获取控件绑定到的窗口句柄。

备注
Handle 属性的值是 Windows HWND。如果句柄尚未创建,引用该属性将强制创建句柄。

在实际中,当一个控件被创建出来后,它的Handle是还没有被创建的,只有当第一次引用了该发生之后,才会被创建。

下面是我写的一段代码。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace InvokeRequiredDemo
{
    public partial class Form1 : Form
    {
        Control controlCreatedFromOtherThread;

public Form1()
        {
            InitializeComponent();
        }

private void Form1_Load(object sender, EventArgs e)
        {
            Thread createControlThread = new Thread(
                new ThreadStart(CreateControlInstance));
            createControlThread.Start();

while (null == controlCreatedFromOtherThread)
            {
                Thread.Sleep(100);
            }

MessageBox.Show(controlCreatedFromOtherThread.InvokeRequired.ToString());
        }

private void CreateControlInstance()
        {
            Control newControlInstance = new Control();
            controlCreatedFromOtherThread = newControlInstance;
        }
    }
}

运行这段代码,对话框抛出的是false。说明主线程是可以调用更新控件的,虽然这个控件是在另一个线程中被创建。

下面我们改一下CreateControlInstance()这个方法。

private void CreateControlInstance()
        {
            Control newControlInstance = new Control();
            IntPtr intPtr = newControlInstance.Handle;
            controlCreatedFromOtherThread = newControlInstance;
        }

我们在代码中加多了一名IntPtr intPtr = newControlInstance.Handle;这句代码将让创建控件的线程(不是主线程)去访问控件的Handle,代码更改后运行,对话框抛出的是true。

关于Control.Handle,还有另一个有趣的东东。比如上面的代码中,你让创建控件的线程同时创建它的Handle,再回到主线程中,这时候,你试着访问它的Handle,会发生什么呢?

private void Form1_Load(object sender, EventArgs e)
        {
            Thread createControlThread = new Thread(
                new ThreadStart(CreateControlInstance));
            createControlThread.Start();

while (null == controlCreatedFromOtherThread)
            {
                Thread.Sleep(100);
            }
            MessageBox.Show(controlCreatedFromOtherThread.Handle.ToString());
        }

答案是抛异常。线程间操作无效: 从不是创建控件“”的线程访问它。

再改改代码:

public delegate void SampleDelegate();

private void Form1_Load(object sender, EventArgs e)
        {
            Thread createControlThread = new Thread(
                new ThreadStart(CreateControlInstance));
            createControlThread.Start();

while (null == controlCreatedFromOtherThread)
            {
                Thread.Sleep(100);
            }

SampleDelegate sampleDelegate = new SampleDelegate(SampleMethod);
            controlCreatedFromOtherThread.Invoke(
                sampleDelegate);
        }

public void SampleMethod()
        {

// Nothing to do

// Please try to make a breakpoint here.

// In this case, no stack here when run the demo.
        }

运行,你会发现,代码根本没有走到SampleMethod()里面去,很奇怪,我在公司时试的结果,是直接在controlCreatedFromOtherThread.Invoke上抛异常的(NullReference,但是我们知道,controlCreatedFromOtherThread并不是空的)。可能是framework或是IDE不一样。公司是VS2005?C#EXPRESS?家里是VS2008。

把controlCreatedFromOtherThread.Invoke改成this.Invoke,SampleMethod()就可以走进去了。

我猜测应该是在用Invoke时,是会需要根据Handle来找到拥有控件的线程,然后在这个线程里调用代理的方法,上面的代码因为主线程拿不到Handle,所以为null,故抛了异常。

最后,再来说说这个Handle是什么时候会被创建。MSDN上说被引用的时候会创建,那什么时候是会被引用呢?直接Control.Handle,当然是,除此之外,还有其它一些case。

比如把控件加到窗体上去,这个控件的Handle也会被窗体所在的线程所创建。事实上,如果控件和窗体的Handle不是在同一样线程上,你是无法把它加到窗体上去的。比如:

private void Form1_Load(object sender, EventArgs e)
        {
            Thread createControlThread = new Thread(
                new ThreadStart(CreateControlInstance));
            createControlThread.Start();

while (null == controlCreatedFromOtherThread)
            {
                Thread.Sleep(100);
            }

this.Controls.Add(controlCreatedFromOtherThread);
        }

我VS2008中,会抛出“线程间操作无效: 从不是创建控件“”的线程访问它。”的异常。在公司时抛出的异常更加详细,大概就是说控件不能被加到窗体中,因为他们的Handle不是在同一个纯种上。

再看下面的例子:

private void Form1_Load(object sender, EventArgs e)
        {
            Thread createControlThread = new Thread(
                new ThreadStart(CreateControlInstance));
            createControlThread.Start();

while (null == controlCreatedFromOtherThread)
            {
                Thread.Sleep(100);
            }

this.Controls.Add(controlCreatedFromOtherThread);
        }

private void CreateControlInstance()
        {
            Control newControlInstance = new Control();

Form newForm = new Form();
            newForm.Controls.Add(newControlInstance);

controlCreatedFromOtherThread = newControlInstance;
        }

注意这里在CreateControlInstance中又创建了一个窗体,并把新创建的控件加到窗体上面去,但是实际上,这个时候控件的Handle还是没有被创建出来,我们运行代码,是不会有异常的。之所以这个,是因为这个新的窗体还没有被Show出来。

再改改:

private void CreateControlInstance()
        {
            Control newControlInstance = new Control();

Form newForm = new Form();
            newForm.Controls.Add(newControlInstance);
            newForm.Show();

controlCreatedFromOtherThread = newControlInstance;
        }

加了newForm.Show(),再运行,异常就出来了。说明新控件的Handle随着窗体的Show也一起被创建。Show方法应该是会先创建窗体的Handle,再创建里面子控件的Handle。

最后补充一点,其实在同一个进程中的不同线程是共享同一堆的,也就是可以共享一片内存。但是为什么不同的线程不能共享控件的Handle呢?我估计是MS特意这样子做,因为如果Handle被共享,在多线程的环境中很容易会出现死锁。

Control controlCreatedFromOtherThread;
        IntPtr controlIntPtrFromOtherThread;

private void Form1_Load(object sender, EventArgs e)
        {
            Thread createControlThread = new Thread(
                new ThreadStart(CreateControlInstance));
            createControlThread.Start();

while (IntPtr.Zero.Equals(controlIntPtrFromOtherThread))
            {
                Thread.Sleep(100);
            }

controlCreatedFromOtherThread = Control.FromHandle(controlIntPtrFromOtherThread);
            MessageBox.Show(controlCreatedFromOtherThread.Name);
        }

private void CreateControlInstance()
        {
            Control newControlInstance = new Control();
            newControlInstance.Name = "hello, leland";
            controlIntPtrFromOtherThread = newControlInstance.Handle;
        }

这个例子中,在非主线程中创建了控件和Handle,并把Handle保存在全局变量中,回到主线程,还是可以根据Handle得到这个控件。

不过,很诡异的事情发生了。上面是把"hello,leland"给了控件的Name属性,在主线程的对话框中,抛出来的还是"hello,leland",this is correct. 我试着把"hello,leland"给Text属性,然而,抛出的是一个空的字符串。。。。

这个例子只是说明不同的线程可以共享一个堆。。关于死锁的例子,就不说了。哈哈。

http://www.360doc.com/content/11/0330/18/6255786_105939099.shtml

InvokeRequired and Invoke相关推荐

  1. c# InvokeRequired和Invoke

    C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它.此时它将会在内部调用ne ...

  2. c# winform InvokeRequired 解决跨线程访问控件

    C#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的,当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它. Windows 窗体中 ...

  3. C# 多线程修改控件时遇到:创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke

    一般在多线程调用UI控件时,涉及到跨线程修改UI,需要使用委托,比如如下: this.Invoke((MethodInvoker)delegate{btnRefresh.Enabled = true; ...

  4. C# async await 学习笔记2

    C# async await 学习笔记1(http://www.cnblogs.com/siso/p/3691059.html) 提到了ThreadId是一样的,突然想到在WinForm中,非UI线程 ...

  5. [转载]分享WCF聊天程序--WCFChat

    http://www.cnblogs.com/gaoweipeng/archive/2009/09/04/1560260.html 无意中在一个国外的站点下到了一个利用WCF实现聊天的程序,作者是:N ...

  6. [转]WinForm下Splash(启动画面)制作

    本文转自:http://www.smartgz.com/blog/Article/1088.asp 原文如下: 本代码可以依据主程序加载进度来显示Splash.     static class Pr ...

  7. C#做的在线升级小程序

    转自原文C#做的在线升级小程序 日前收到一个小任务,要做一个通用的在线升级程序.更新的内容包括一些dll或exe或.配置文件.升级的大致流程是这样的,从服务器获取一个更新的配置文件,经过核对后如有新的 ...

  8. c#中对Cross-thread operation not valid错误的处理办法

    目录 目录 概要 1. Example 1.1 Unsafe access to control 1.2 What's mean? 2. The first choice : CheckForIlle ...

  9. C# 实现基于ffmpeg加虹软的人脸识别

    2019独角兽企业重金招聘Python工程师标准>>> 关于人脸识别 目前的人脸识别已经相对成熟,有各种收费免费的商业方案和开源方案,其中OpenCV很早就支持了人脸识别,在我选择人 ...

最新文章

  1. [MySQL] 几句MySQL时间筛选SQL语句[进入查看]
  2. 解决Eclipse查看源代码出现Source not found的问题
  3. android快速打包工具下载,【Android】多渠道打包,其实可以更快
  4. QML笔记-键盘事件中同时响应onDigitXXPressed与onPressed
  5. Linux多线程同步
  6. mysql 快速初始化_MySQL中的批量初始化数据的对比测试(r12笔记第71天)
  7. 单片机shell命令_nr_micro_shell
  8. 实验5.3 编程实现两字符串的连接(使用字符数组)
  9. 程序员修炼之道:从小工到专家
  10. python requests text content_对python requests的content和text方法的区别详解
  11. TI DSP处理器中CMD 文件的那些事儿
  12. PF_PACKET说开去
  13. TF-IDF算法总结
  14. 记一次学习爬取豆瓣数据于Excel表的爬虫
  15. Unity表情聊天(NGUI图文混排)
  16. 淘宝原数据商品详情API调用示例
  17. 新农慕课python小测验答案_python面向对象程序设计_章节测验,期末考试,慕课答案查询公众号...
  18. Jenkins邮箱配置中,使用SSL连接的问题
  19. 号)、sex(性别)、birthday(出生日期)、id(身份证号)等等。其中“出生日期”定义为一个“日期”类内嵌子对象。用成员函数实现对人员信息的录入和显示。要求包括:构造函数和析构函数、拷贝构造函
  20. 在线扫描php后门_webshell后门扫描-PHP版

热门文章

  1. 2022-6-4 小明爱上课,切木头,最多分成多少块,躲猫猫,争渡
  2. openlayers地图实现地点标注
  3. pytorch 猫狗二分类 resnet
  4. sql char和varchar的区别
  5. 计算机主机无反应,电脑突然开不了机、主机没反应、不显示,几个方法轻松解决...
  6. MPU6050 6轴姿态传感器的分析与使用(一)
  7. LabVIEW学习分享(2)
  8. phd计算机考试,21校计算机Phd详细申请经验
  9. Linux查看磁盘空间的命令
  10. jmeter之取样器(HTTP请求、调试取样器)