Winform消息与并行的形象比喻
有一次我给同事讲述跨线程调用时使用了高速行驶的并行列车来比喻,感觉比较形象。
线程列车
多线程就像多个并行的列车,每个线程在各自的轨道上不断向前行驶。主界面所在的线程称为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消息与并行的形象比喻的更多相关文章
- 加密解密(5)SSL形象比喻
转自: http://blog.csdn.net/cloverphp/article/details/11737433 前言: 关于公钥,私钥请看前几篇文章 SSL 协议既用到了公钥加密技术(握手 ...
- 大数据技术生态圈形象比喻(Hadoop、Hive、Spark 关系)
[摘要] 知乎上一篇很不错的科普文章,介绍大数据技术生态圈(Hadoop.Hive.Spark )的关系. 链接地址:https://www.zhihu.com/question/27974418 [ ...
- winform消息提示框
摘自:http://www.cnblogs.com/risen/archive/2008/01/15/1039751.html public partial class AlertForm : For ...
- ajax获取数据的形象比喻,助于理解记忆
过程 创建对象(打开浏览器) 连接服务器(输入网址) 发送请求(按下回车) 服务器接收并返回数据(显示对应的网址网站内容) 原理
- I/O存取方式的形象比喻
I/O存取有三种方式:可编程I/O.中断驱动I/O.DMA,分别可理解如下: 下面以老师向班里同学收发作业来类比I/O存取,办公室表示内存,即,I操作表示:老师向学生收作业,然后存放到办公室里:O操作 ...
- NIO与传统IO的区别(形象比喻)[转]
传统的socket IO中,需要为每个连接创建一个线程,当并发的连接数量非常巨大时,线程所占用的栈内存和CPU线程切换的开销将非常巨大.使用NIO,不再需要为每个线程创建单独的线程,可以用一个含有限数 ...
- BIO、NIO、AIO的形象比喻
BIO (Blocking I/O):同步阻塞I/O模式. NIO (New I/O):同步非阻塞模式. AIO (Asynchronous I/O):异步非阻塞I/O模型. 先看阻塞和非阻塞的区别, ...
- Windows服务调用Quartz.net 实现消息调度
Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...
- C#.NET 消息机制
一.消息概述 众人周知,window系统是一个消息驱动的系统, windows操作系统本身有自己的消息队列,消息循环,它捕捉键盘,鼠标的动作生成消息,并将这个消息传给应用程序的消息队列. 余下的工作有 ...
随机推荐
- 如何识别Studio 5000程序开发版本号
前言:中.大型AB PLC的编程软件从以前的RSLogix 5000到目前的Studio 5000,都是有版本号的,如RSLogix 5000 V19.0.Studio 5000 V32.高版本的软件 ...
- 常用软件记录 ( Windows )
带括号注明的为主力软件 现安装的: 360驱动大师 7-Zip Acrobat Reader DC Adobe Photoshop 7.0 Anaconda babun Bandizip (解压) b ...
- linux Centos7下安装python3及pip3
先去python官网下载python3安装包 执行命令:wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz 安装zlib-dev ...
- selenium + python自动化测试(一)
本篇随笔为散集.不多逼逼直接干 step 1:使用webdriver调用浏览器,webdriver支持的浏览器为Firefox(),IE(),Chrome(),不同的浏览器需要安装不同的驱动. 本文使 ...
- delphi 获取时间戳 如何得到 和 js 中 new Date().getTime();的 相同?
new Date().getTime(); //1533213439019 通过,启发 function DateTimeToUnix(const AValue: TDateTime): Int64 ...
- oracle ORA-02292: 违反完整约束条件
我是处于工作中没用过oracle的状态,这不,记录下这个小小的问题.哈哈. 表是公司的平台组定义的.前几天为了测试程序,想删掉一些记录,然后使用delete语句,出现这个东东:oracle ORA-0 ...
- dos.orm的事务处理
dos.orm也包含事务处理,没有太多封装,这里有几个简单的示例代码. using (DbTrans trans = DbSession.Default.BeginTransaction()) { D ...
- 如何用java实现一个p2p种子搜索(4)-种子获取
种子获取 在上一篇中我们已经可以获取到dht网络中的infohash了,所以我们只需要通过infohash来获取到种子,最后获取种子里面的文件名,然后和获取到的infohash建立对应关系,那么我们的 ...
- Jenkins的配置从节点中默认没有Launch agent via Java Web Start选项问题
Jenkins的配置从节点中默认没有Launch agent via Java Web Start,如下图所示,而这种启动方式在Windows上是最方便的. 如何设置才能让出来呢? 1:打开" ...
- NB学校的NB课程的NB教材——CSAPP
CMU是全美以至全球公认的CS最猛的大学之一,没办法,作为CS的发源地,再加上三位神一样的人先后在此任教:Alan Perlis(CS它祖宗+第一届Turing奖获得者).Allen Newell(A ...