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

线程列车

多线程就像多个并行的列车,每个线程在各自的轨道上不断向前行驶。主界面所在的线程称为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. spring 4 + hibernate 4 配置数据库事务

    配置事务时应该加载aopalliance-1.0.jar和aspectjweaver.jar这两个包,这两个包是必须的.

  2. 2018-2019-2 网络对抗技术 20165237 Exp6 信息搜集与漏洞扫描

    2018-2019-2 网络对抗技术 20165237 Exp6 信息搜集与漏洞扫描 实验目标 1 各种搜索技巧的应用: 2 DNS IP注册信息的查询: 3 基本的扫描技术: 主机发现.端口扫描.O ...

  3. # 20175333曹雅坤《Java程序设计》第四周学习总结

    教材学习内容总结 第五章:子类与继承 5.1子类与父类:关键字extends 5.2子类的继承性:如果子类与父类在一个包中,除了private其他都可以继承:如果不在一个包中,则private和友好都 ...

  4. 结合jira搭建自动化测试平台

    mysql 语句查看 python manage.py sqlmigrate your_app_name 0001 代码如下 #coding=utf8 #https://jira.readthedoc ...

  5. Java 基本语法,标识符,修饰符,关键字

    基本语法 编写 Java 程序时,应注意以下几点: 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的. 类名:对于所有的类来说,类名的首字母应该大写.如果类 ...

  6. 【JS】VUE学习

    VUE的全家桶:vue-cli,vue-router,vue-resource,vuex 环境搭建:https://www.jianshu.com/p/32beaca25c0d 先码在这儿吧. htt ...

  7. MySQL ERROR 1054(42S22)

    修改用户的密码,网上搜到的命令为如下 执行后报错 ERROR 1054(42S22) Unknown column 'password' in ‘field list’ 错误的原因是 5.7版本下的m ...

  8. vue学习(二)

  9. python-循环&运算符

    一.while 循环语句 while 循环语句的基本用法如下: while 条件表达式: 循环体 当条件表达式的返回值为真时,则执行循环体中的语句,执行完毕后,重新判断条件表达式的返回值,直到表达式的 ...

  10. IntelliJ IDEA 如何设置类头注释和方法注释

    从VS转过来的,ide的差距很大的,所以...特意折腾了很久,结果还是没有VS的 '///' 好用 一.类头注释 打开file -> setting -> Editor -> Fil ...