有一次我给同事讲述跨线程调用时使用了高速行驶的并行列车来比喻,感觉比较形象。

线程列车

多线程就像多个并行的列车,每个线程在各自的轨道上不断向前行驶。主界面所在的线程称为UI线程,也叫主线程,主线程依靠消息驱动,可以将主线程的列车每节车厢想象为一个消息,每次转换并处理一个消息,处理过程中如果有新的消息不会马上处理而是放入一个消息队列,等下一轮处理。

例如我在屏幕上点击一个按钮,操作系统将鼠标的按下抬起等消息推送到消息队列中。程序主线程的下一轮开始转换这个消息然后处理这个消息,发送给指定窗口。假设我们在点击消息处理方法中进行一些界面更新,并调用了Invalidate,此时只是发出了消息,然后继续执行后续代码,当点击消息处理完毕后,才会从消息队列获取下一个消息处理。

对于跨线程操作的Invoke,可以这么理解。就是并行列车在高速行驶中如果直接调用另一个列车上的方法是非常危险的,我们坐车的时候售票员总是提醒我们不要把头和手伸出窗外是一个道理。所以,假如我们在一个主线程之外的线程列车上想要UI线程去执行一个方法,此时我们需要将方法包装成委托,然后通过Control.Invoke给主线程发送消息,主线程会在下一次消息处理时处理我们的消息,由被调用的Control在UI线程执行我们的方法。

如果我们在Invoke后,还需要处理返回值,那么我们自己所在的列车就不能继续开了,要停下列车,等主线程的列车处理完我们的方法,返回结果,并通过消息发送回来,我们收到返回的消息时,才继续开动列车处理后续消息。也就是使用Invoke的返回的WaitHandle的WaitOne方法等待了。

需要理解Windows的消息驱动机制。我们知道任意时刻执行的代码一定是处于一个消息中,或者是空闲事件消息中。消息也是跨线程调用的基本机制。

Control.BeginInvoke是从线程池启动一个线程执行,相对主线程是异步的。Control.Invoke则是在其他线程中回到UI线程执行。但这两种方式都不是推荐的最优做法,推荐用TPL模式,就是使用Task来进行异步。需要回到主线程时用AsyncOperation,原理是一样的还是发消息,只是AsyncOperation会发送给一个必定存在的句柄,避免线程安全问题。

句柄问题

另一个很多人不明白的问题就是窗口句柄何时创建,以及OnLoad的时机。其实,Winform程序是对本地代码的包装而已,底层还是过程式语言的API调用。过程语言通过句柄来唯一标识所有的本地资源,所有的方法都需要传入句柄 。而我们创建的控件类其实并不是真正的可见的类,翻看C++版本的代码就可以知道,其实还是调用API来CreateWindow,此时传入的类名才是API中所指的类名,此时传入的参数在Control里使用了CreateParam结构体和CreateParam方法来实现。

简单的说吧,当我们创建一个Button时,只是调用了Button的构造方法而已,并没有在屏幕上可见,当我们调用Parent的AddControl时,才会去创建句柄,此时才会触发控件的OnCreateControl,如果控件是一个UserControl才会触发OnLoad事件。Control的OnCreateControl和UserControl的OnLoad是同一个时机发生的。只有创建了句柄才会在屏幕上绘制出来,当父窗体隐藏时,所有子控件的句柄会销毁,因为不用绘制了,而再次Show时,会重新创建句柄。

Winform消息与并行的形象比喻的更多相关文章

  1. 加密解密(5)SSL形象比喻

    转自: http://blog.csdn.net/cloverphp/article/details/11737433 前言: 关于公钥,私钥请看前几篇文章   SSL 协议既用到了公钥加密技术(握手 ...

  2. 大数据技术生态圈形象比喻(Hadoop、Hive、Spark 关系)

    [摘要] 知乎上一篇很不错的科普文章,介绍大数据技术生态圈(Hadoop.Hive.Spark )的关系. 链接地址:https://www.zhihu.com/question/27974418 [ ...

  3. winform消息提示框

    摘自:http://www.cnblogs.com/risen/archive/2008/01/15/1039751.html public partial class AlertForm : For ...

  4. ajax获取数据的形象比喻,助于理解记忆

    过程 创建对象(打开浏览器) 连接服务器(输入网址) 发送请求(按下回车) 服务器接收并返回数据(显示对应的网址网站内容) 原理

  5. I/O存取方式的形象比喻

    I/O存取有三种方式:可编程I/O.中断驱动I/O.DMA,分别可理解如下: 下面以老师向班里同学收发作业来类比I/O存取,办公室表示内存,即,I操作表示:老师向学生收作业,然后存放到办公室里:O操作 ...

  6. NIO与传统IO的区别(形象比喻)[转]

    传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大时,线程所占用的栈内存和CPU线程切换的开销将非常巨大.使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数 ...

  7. BIO、NIO、AIO的形象比喻

    BIO (Blocking I/O):同步阻塞I/O模式. NIO (New I/O):同步非阻塞模式. AIO (Asynchronous I/O):异步非阻塞I/O模型. 先看阻塞和非阻塞的区别, ...

  8. Windows服务调用Quartz.net 实现消息调度

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...

  9. C#.NET 消息机制

    一.消息概述 众人周知,window系统是一个消息驱动的系统, windows操作系统本身有自己的消息队列,消息循环,它捕捉键盘,鼠标的动作生成消息,并将这个消息传给应用程序的消息队列. 余下的工作有 ...

随机推荐

  1. Pytorch学习笔记(二)---- 神经网络搭建

    记录如何用Pytorch搭建LeNet-5,大体步骤包括:网络的搭建->前向传播->定义Loss和Optimizer->训练 # -*- coding: utf-8 -*- # Al ...

  2. Burp插件开发——环境配置

    最近打算开发个Burp插件,从网上各种地找资料学习.第一步就应该是环境配置,请见下文. (其实最重要的前提是你已经安装了Burp,否则下面的所有内容都是无稽之谈了. https://pan.baidu ...

  3. 【译】索引进阶(十三):SQL SERVER中的索引碎片【下篇】

    原文链接:传送门. 通用碎片模式 如果一个表具有多个索引,那么这些索引便具有多个索引键,因而也具有不同的顺序.通常情况下,这些索引中的一两个展示了之前描述过的升序插入模式,而其他的通常展示了随机插入模 ...

  4. java - day006 - 构造方法

    构造方法 新建对象是,执行的一个特殊方法 new 类名(); 这个小括号就是构造方法 Java 类中,必须有构造方法,如果自己不定义, 编译器会自动生成默认构造方法 new 关键字是新建对象用的,   ...

  5. String,下表和切片,分割

    字符串介绍 1.字符串在内存中的存储 2.字符串的加法 3.字符串的格式化 1. 下标索引 所谓“下标”,就是编号,就好比超市中的存储柜的编号,通过这个编号就能找到相应的存储空间 字符串中" ...

  6. vue学习(二)

  7. DataTable插件通过js导出Excel

    $('#myTab').DataTable( { serverSide: false,//分页,取数据等等的都放到服务端去. true为后台分页,每次点击分页时会请求后台数据,false为前台分页 d ...

  8. Interactive map of Linux kernel

    Interactive map of Linux kernel 2.6.36  : http://www.makelinux.net/kernel_map/ 注: 图中函数名带连接

  9. Runtime个别API的使用

    Runtime 关于属性部分API的说明以及使用方法 使用Runtime机制需要引入头文件: #import <objc/runtime.h> 1:  Ivar *class_copyIv ...

  10. Python脚本日志系统

    Python通过logging模块提供日志功能,关于logging模块的使用网络上已经有很多详细的资料,这里要分享的是怎样在实际工程中使用日志功能. 假设要开发一个自动化脚本工具,工程结构如下,Com ...