基于消息的GUI构架

在过去的日子中,大部分编程语言平台的GUI构架几乎没有发生变化。虽然在细节上存在一些差异,比如在功能和编程风格上,但大部分都是采用了相同的构架来响应用户输入以及重新绘制屏幕。这种构架可以被总结为“单线程且基于消息”。

 
Message msg;While(GetMessage(msg))
{TranslateMessage(msg);DispatchMessage(msg);
}

这段代码可以称为消息循环。在这个循环中,执行顺序是串行的,一个GetMessage只能在前一个GetMessage执行完以后才能执行。

拿WPF或WindowsForm举例,每个线程至少会创建一个拥有消息列队的窗口,并且这个线程的任务之一就是处理列队中的各个消息。只要在应用程序中调用了Application.Run,那么执行这个方法的线程就默认被赋予了这个任务。随后的所有GUI事件,例如用户引发的事件(点击按钮,关闭窗口等),系统引发的事件(重绘窗口,调整大小等),以及应用程序中自定义组件的特定事件等,都将把相应的消息投递给这个消息列队来实现。这意味着,在调用了run之后,随后发生的大部分工作都是由事件处理器为了响应GUI事件而生成的。

如图:

GUI线程

Gui线程负责取走(get)和分发(dispatch)消息,同时负责描绘界面,如果GUI线程阻塞在分发处理消息这一步的话,那么消息就会在消息队列中积累起来,并等待GUI线程回到消息列队来。

如果阻塞的是一个长时间的操作,比如下载一个文件的话,假设10秒钟,那么用户在10秒钟内都不能进行任何操作,因为线程没法获取新的消息进行处理。

这就是为什么在Windows中存在着MsgWaitForMultipleObjects的原因,这个API使得线程在等待的同时仍然可以运行消息循环。在.NET中,你连这个选择都没有。

消息分发时要考虑到复杂的重入性问题,很难确保一个事件处理器阻塞时,可以安全分发其他GUI事件以响应消息。

因此,一种相对而言更容易掌握的解决方法就是只有在GUI线程中的代码才能够操纵GUI控件,在更新GUI时所需要的其他数据和计算都必须在其他线程中完成,而不是在GUI线程上。如图:

通常这意味着把工作转交给线程池完成,然后在得到结果后把结果合并回GUI线程上。这也就是我们接下来要介绍的两个类。

SynchronizationContext 和 BackgroundWorker

SynchronizationContext 对不同线程间的调度操作进行同步,把一些异步操作的结果Post回GUI线程里。

WPF中DispatcherSynchronizationContext的实现

public  override  void  Post(SendOrPostCallback  d,  object  state)
{ _dispatcher.Beginlnvoke(DispatcherPriority.Normal,  d,  state); } public  override  void  Send(SendOrPostCallback  d,  object  state) { _dispatcher.lnvoke(DispatcherPriority.Normal,  d,  state);
} 

有些情况下,如在控制台中我们不能通过SynchronizationContext类的Current属性获取SynchronizationContext实例,我们包装了一下这个方法。

private static AsyncCallback SyncContextCallback(AsyncCallback callback) {// Capture the calling thread's SynchronizationContext-derived objectSynchronizationContext sc = SynchronizationContext.Current;// If there is no SC, just return what was passed inif (sc == null) return callback;// Return a delegate that, when invoked, posts to the captured SC a method that // calls the original AsyncCallback passing it the IAsyncResult argumentreturn asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
}

这个方法将一个普通的AsyncCallback方法转换成特殊的AsyncCallback 方法,它通过SynchronizationContext 来调用。这样无论线程模型中是否含有GUI线程,都可以正确的调用。

internal sealed class MyWindowsForm : Form {public MyWindowsForm() {Text = "Click in the window to start a Web request";Width = 400; Height = 100;}protected override void OnMouseClick(MouseEventArgs e) {// The GUI thread initiates the asynchronous Web request Text = "Web request initiated";var webRequest = WebRequest.Create("http://Wintellect.com/");webRequest.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webRequest);base.OnMouseClick(e);}private void ProcessWebResponse(IAsyncResult result) {// If we get here, this must be the GUI thread, it's OK to update the UIvar webRequest = (WebRequest)result.AsyncState;using (var webResponse = webRequest.EndGetResponse(result)) {Text = "Content length: " + webResponse.ContentLength;}}
}

这其实就是AsyncOperationManager的基本原理。

public  static  class  AsyncOperationManager
{ public  static  SynchronizationContext  {  getj  setj  } public  static  AsyncOperation  CreateOperation( object  userSuppliedState ) ;
}

BackGroundWorker是在前面所说的基础上构建起来的更高层次的抽象,它对GUI程序中一些最常用的操作给出了规范的定义。有三个事件:

DoWork 、ProgressChanged 和 RunWorkerCompleted

在程序中调用RunWorkerAsync方法则会启动DoWork事件的事件处理,当在事件处理过程中,调用 ReportProgress方法则会启动ProgressChanged事件的事件处理,而当DoWork事件处理完成时,则会触发 RunWorkerCompleted事件。

    public partial class Form1 : Form{public Form1(){InitializeComponent();backgroundWorker1.WorkerReportsProgress = true;backgroundWorker1.WorkerSupportsCancellation = true;}private void startAsyncButton_Click(object sender, EventArgs e){if (backgroundWorker1.IsBusy != true){// Start the asynchronous operation.
                backgroundWorker1.RunWorkerAsync();}}private void cancelAsyncButton_Click(object sender, EventArgs e){if (backgroundWorker1.WorkerSupportsCancellation == true){// Cancel the asynchronous operation.
                backgroundWorker1.CancelAsync();}}// This event handler is where the time-consuming work is done.  private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {BackgroundWorker worker = sender as BackgroundWorker;for (int i = 1; i <= 10; i++){if (worker.CancellationPending == true){e.Cancel = true;break;}else{// Perform a time consuming operation and report progress.System.Threading.Thread.Sleep(500);worker.ReportProgress(i * 10);}}}// This event handler updates the progress.  private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {resultLabel.Text = (e.ProgressPercentage.ToString() + "%");}// This event handler deals with the results of the background operation.  private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {if (e.Cancelled == true){resultLabel.Text = "Canceled!";}else if (e.Error != null){resultLabel.Text = "Error: " + e.Error.Message;}else{resultLabel.Text = "Done!";}}}

转载于:https://www.cnblogs.com/lwzz/archive/2012/11/17/2754011.html

多线程之旅七——GUI线程模型,消息的投递(post)与处理相关推荐

  1. C#多线程之旅(七)——终止线程

    阅读目录 一.什么时候用Thread.Abort(); 二.Thread.Abort的用法 三.无法终止线程的情形 四.Catch块中抛出异常 五.Finally块中抛出异常 六.Abort调用的时间 ...

  2. Netty系列(2)快速入门Netty线程模型、Netty入门程序、Netty任务队列

    文章目录 1 Netty线程模型 1.1 传统阻塞 I/O 服务模型 1.2 Reactor线程模型 1.2.1 单 Reactor 单线程模型 1.2.2 单Reactor多线程 1.2.3 主从 ...

  3. muduo网络库:18---muduo简介之(muduo库的由来、编译安装、目录结构、代码结构、线程模型)

    一.由来 2010年3月陈硕先生写了一篇<学之者生,用之者死--ACE历史与简评>(文章参阅:https://blog.csdn.net/Solstice/article/details/ ...

  4. 多线程之旅之四——浅谈内存模型和用户态同步机制

     用户态下有两种同步结构的 volatile construct: 在简单数据类型上原子性的读或者写操作   interlocked construct:在简单数据类型上原子性的读和写操作 (在这里还 ...

  5. videojs如何获取请求消息_中通消息平台 Kafka 顺序消费线程模型的实践与优化

    各类消息中间件对顺序消息实现的做法是将具有顺序性的一类消息发往相同的主题分区中,只需要将这类消息设置相同的 Key 即可,而 Kafka 会在任意时刻保证一个消费组同时只能有一个消费者监听消费,因此可 ...

  6. QT 多线程的实现方法以及GUI线程与其他线程间的通信

    GUI线程 Qt应用程序exec后就会生成一个线程,这个线程就是主线程,在GUI程序中也称为GUI线程.主线程也是唯一允许创建QApplication或QCoreAppliation对象,比并且可以对 ...

  7. Java 多线程(七) 线程间的通信

    Java 多线程(七) 线程间的通信--wait及notify方法 线程间的相互作用 线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务. Object类中相关的方法有两个notify方法 ...

  8. Netty实战七之EventLoop和线程模型

    简单地说,线程模型指定了操作系统.编程语言.框架或者应用程序的上下文中的线程管理的关键方面.Netty的线程模型强大但又易用,并且和Netty的一贯宗旨一样,旨在简化你的应用程序代码,同时最大限度地提 ...

  9. java多线程模型_1、java线程模型

    要了解多线程,先需要把java线程模型搞清楚,否则有时候很难理清楚一个问题. 硬件多线程: 物理机硬件的并发问题跟jvm中的情况有不少相似之处,物理机的并发处理方案对于虚拟机也有相当大的参考意义.在买 ...

最新文章

  1. 45. Jump Game II
  2. apache php value,apache-2.2 – 错误启动Apache“php value”
  3. c语言怎么让写的函数兼容int型和char型_既然C语言void指针是“万能指针”,那么malloc还需类型转换吗?...
  4. 网站集成支付宝接口安装教程
  5. 4月28日见!努比亚红魔3电竞手机曝光:90Hz屏幕刷新率加持
  6. go语言 gosched
  7. Oracle 抢人了!近 4000 万年薪只为一个 AI 专家
  8. asp.net 通过IHttpModule开发接口
  9. Java基础知识笔记第八章:常用的实体类
  10. c语言编译器模拟流水灯,流水灯C语言代码(修改)!
  11. 阿里2018届实习生内推经历
  12. 欧拉回路和Hanmilton回路
  13. Win10下SQL2000 企业管理器 新建表/修改表 崩溃问题的应急
  14. java的 %3c%3c 运算符_scanf(%3c%3c, a, b)
  15. 【POI每日题解 #5】 DWU-Double-row
  16. Error in melt() : could not find function “melt“
  17. Python【queue】
  18. Policy gradient(策略梯度详解)
  19. 关于discuz论坛邮箱配置
  20. redflag Dc5.0,Asianux2安装时无法创建文件系统,缺少scsi驱动解决办法

热门文章

  1. The content of element type web-app must match……等 问题之解决办法
  2. ubuntu16 kickstart pxe 安装系统
  3. 记HTML5 a 标签的一个小坑 1
  4. 远程调用——hessian使用入门
  5. css在线工具_已迁移
  6. cocos2d-x 观察者模式
  7. oracle性能优化总结1
  8. 20181027_任务
  9. java获取中文拼音首字母
  10. 第一百一十八天 how can I 坚持