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

线程列车

多线程就像多个并行的列车,每个线程在各自的轨道上不断向前行驶。主界面所在的线程称为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. Python+Selenium+Unittest+HTMLTestRunner生成测试报告+发送至邮箱,记一次完整的cnblog登录测试示例,

    测试思路:单个测试集.单个测试汇成多个测试集.运行测试集.生成测试报告.发送至邮箱. 第一步:建立单个测试集,以cnblog登录为例. 测试用例: cnblog的登录测试,简单分下面几种情况:(1)用 ...

  2. [转]ANR问题分析指南

    引言 每天收到无数的兄弟团队的同事向系统转ANR JIRA,有些一旦遇到App ANR就直接转到系统组,有些简单看一下就转到系统组帮忙看一下.如此浩瀚的JIRA,我们什么事不做也处理不过来,请每个Ap ...

  3. 前端使用moment.js 获取当前时间往前的时间

    moment().format("YYYY-MM-DD HH:mm:ss"); //当前时间 moment().subtract(, "days").forma ...

  4. Java位运算原理及使用讲解

    前言日常开发中位运算不是很常用,但是巧妙的使用位运算可以大量减少运行开销,优化算法.举个例子,翻转操作比较常见,比如初始值为1,操作一次变为0,再操作一次变为1.可能的做法是使用三木运算符,判断原始值 ...

  5. MySQL1:客户端/服务器架构

    一.MySQL的客户端/服务器架构 前言 之前对MySQL的认知只限于会写些SQL,本篇算是笔记,记录和整理下自己对MySQL不熟悉的地方. 大致逻辑: MySQL的服务器程序直接和我们存储的数据打交 ...

  6. 增加swap分区

    起因:开发人员说tomcat关闭了,然后我排查了下,发现内存耗尽,然后临时用swap分区,以供当前运行的程序使用. 先用free -h查看一下swap的大小 1.添加swap分区 使用dd命令创建/h ...

  7. powerdesigner添加mysql的字符集ENGINE和DEFAULT CHARACTER SET

    工具栏->database->edit current DBMS 然后,选中:MYSQL50::Script\Objects\Table\Options 在options末尾添加: ENG ...

  8. docker环境下elasticsearch安装ik和拼音分词

    elasticsearch拼音分词地址:https://github.com/medcl/elasticsearch-analysis-pinyin/releases 在elasticsearch下面 ...

  9. U盘安装Mac OS X要点

    1.启动U盘必须比系统磁盘小,因为制作启动U盘,U盘很可能被当成系统盘.系统安装时,发现系统盘比U盘小,很可能提示安装失败. 2.制作启动U盘.详情查看官网:https://support.apple ...

  10. bootstrap学习(一)栅格、布局

    栅格: 栅格将整个屏幕分为12份,当使用单一的一组 .col-md-* 栅格类,就可以创建一个基本的栅格系统,在手机和平板设备上一开始是堆叠在一起的(超小屏幕到小屏幕这一范围),在桌面(中等)屏幕设备 ...