WPF与WinForm对比谈 多线程编程优化是关键

开发 后端
对于WPF,很多人的观点还是觉得它就是WinForm上加了一层皮。本文作者之前也是同样的理解,不过在深入了解后,他发现其实二者最主要的区别就在于多线程编程优化。

51CTO之前也曾报道过《Visual Studio 2010 RC中在WPF开发方面有多个不同之处》,那么看来WPF还是有其可取之处,希望本文能对大家有所帮助。

很多人问过我这样一个问题:WPF和以前的WinForm有什么区别?

我之前的回答一直是:没什么区别,仅仅是表示层用XAML封了层皮,使得Windows看起来更炫了。

今天(确切的说是昨天),我终于发现了我肤浅。首先我要澄清一下,WPF较之WinForm的先进之处不止一点点。

对于WPF,很多人都以为这是微软的一个小玩具,充其量就是让Vista和Win7的表示层更炫了,然后就会吸引人们去购买——当然了,我相信多数人购买Wim7的动机并不在乎它的内核做了什么变动...

言归正传...

WPF将Windows表示层发展至用声明式语言进行开发,并且融入大量的动画和特效,使得在Win32中极难做到的富客户端应用能在WPF中信手拈来,同时用矢量图取代位图,引入路由事件(RoutedEvent)对元素树进行多层监听,通过依赖属性(DependencyProperty)动态变更控件树等,这些都是表面上我们所看到的。不过这些不是我今天想说的重点,何况有不少牛人的研究要比我深入多了,实在是自惭形秽啊,今天我只是承认错误来的。

在众多WPF对于WinForm的优势中,令我意识到我之前的错误的一点就是WPF对于多线程编程的优化。

在WinForm程序开发时,一旦涉及多线程操作,我们一般不可能没有见过InvalidOperationException这个异常。这个异常的出现多数情况是由于worker线程(子线程)修改主线程控件(或对象)的属性而导致的非法操作,当然这种做法也非每次都会失败,这主要取决于子线程想要操作的对象是不是线程安全的。

无论是Windows窗体还是WPF,问题的成因都很简单:Windows控件使用的是组件对象模型(Component Object Model,COM)单线程单元(Single-threaded Apartment,STA)模型,因为其底层的控件是单元线程(apartment-threaded)的。此外,很多控件都用消息泵(message pump)来完成操作。因此,这种模型就需要所有调用该控件的方法都和创建该控件的方法位于同一个线程上。

WinForm控件提供了InvokeRequired属性来判断当前线程是不是创建此控件的线程。一旦控件创建完成,那么InvokeRequired的效率将会不错,且也能保证安全。不过若是目标控件尚未被创建(此时,虽然C#对象已经存在,不过其底层的窗口句柄仍旧为null),那么InvokeRequired则可能会耗费比较长的时间。(它会从下至上遍历整个控件树,直到找到一个可以承载此控件且已经实例化并被创建了的父控件,这种做法可以保证子控件将会与父控件在同一个线程上创建。找到合适的父控件之后,框架即可执行同样的检查,比较当前线程的ID和创建该父控件的线程的ID。)。若是框架无法找到任何一个已创建的父窗体,那么则需要找到一些其他类型的窗体。若在层次体系中无法找到可用的窗体,那么框架将开始寻找暂存窗体(parking window),暂存窗体让你不会被某些Win32 API奇怪的行为所干扰。简而言之,有些对窗体的修改(例如修改某些样式)需要销毁并重新创建该窗体。暂存窗体就是用来在父窗体被销毁并重新创建的过程中用来临时保存其中的控件的。在这段时间内,UI线程仅运行于暂存窗体中。

“通常,WPF 应用程序从两个线程开始:一个用于处理呈现,一个用于管理 UI。呈现线程有效地隐藏在后台运行,而 UI 线程则接收输入、处理事件、绘制屏幕以及运行应用程序代码。”

“UI 线程对一个名为 Dispatcher 的对象内的工作项进行排队。 Dispatcher 基于优先级选择工作项,并运行每一个工作项,直到完成。每个 UI 线程都必须至少有一个 Dispatcher,并且每个 Dispatcher 都只能在一个线程中执行工作项。”

                                                                                                                                ——MSDN

WPF开始设计的时候,就将多线程的问题考虑了进去,上述很多过程都得到了简化。这得益于Dispatcher类的使用,每个线程都有一个Dispatcher。在第一次访问某个控件的Dispatcher时,类库将察看该线程是否已经拥有了Dispatcher。若已经存在,那么直接返回。如果没有的话,那么将创建一个新的Dispatcher对象,并关联在控件及其所在的线程之上。Dispatcher提供了类似InvokeRequired的方法(CheckAccess)【实际上并不提倡使用此方法,且在目前的WPF中此方法已经被取消】。这个方法只是比较线程的ID,所以会很快。

另外,Dispatcher提供了优先队列(总共11个Priority,主要是用于WPF中UI的层次结构设计,比如动画的优先级就是最高的);异步调用(WinForm中的Control.BeginInvoke和Control.EndInvoke也提供异步功能);DispatcherTimer (使用 DispatcherTimer 而不是使用 System.Timers.Timer 的原因是 DispatcherTimer 与 Dispatcher 运行于相同的线程,并且可以在 DispatcherTimer 上设置 DispatcherPriority),简化了开发多线程GUI程序。

WPF延伸阅读

WPF框架图

WPF框架图

WPF为Windows Presentation Foundation的缩写 ,其原来代号为“Avalon”,因“我佩服”拼音首字母组合一样,国内有人调侃地称之为“我佩服”。WPF是微软新一代图形系统,运行在.NET Framework 3.0架构下,为用户界面、2D/3D 图形、文档和媒体提供了统一的描述和操作方法。基于DirectX 9/10技术的WPF不仅带来了前所未有的3D界面,而且其图形向量渲染引擎也大大改进了传统的2D界面,比如Vista中的半透明效果的窗体等都得益于WPF。 程序员在WPF的帮助下,要开发出媲美Mac程序的酷炫界面已不再是遥不可及的奢望。 WPF相对于Windows客户端的开发来说,向前跨出了巨大的一步,它提供了超丰富的.NET UI 框架,集成了矢量图形,丰富的流动文字支持flow text support,3D视觉效果和强大无比的控件模型框架。

WinForm延伸阅读

WinForm是.Net开发平台中对Windows Form的一种称谓。.Net 为开发WinForm的应用程序提供了丰富的Class Library(类库)。这些WinForm 类库支持RAD(快速应用程序开发),这些类库被封装在一个名称空间之中,这个名称空间就是System.Windows.Forms。在此名称空间中定义了许多类,在开发基于.Net的GUI应用程序的时候,就是通过继承和扩展这些类才使得我们的程序有着多样的用户界面。

原文标题:关于WPF,我个人长期的误解

链接:http://www.cnblogs.com/iamzhyk/archive/2010/04/07/1705903.html

【编辑推荐】

  1. 探讨WPF开发工作站相关知识
  2. WPF Dispatcher在界面操作中作用体现
  3. 实现WPF淡入效果方式技巧分享
  4. WPF Listbox生成方式介绍
  5. WPF边框不同形式的设置技巧 
责任编辑:彭凡 来源: 博客园
相关推荐

2018-10-25 15:55:44

Java多线程锁优化

2010-01-07 09:53:09

Winform多线程编

2013-07-16 10:12:14

iOS多线程多线程概念多线程入门

2023-02-20 15:29:46

异步编码多线程

2023-06-13 13:39:00

多线程异步编程

2023-11-01 11:20:57

2009-03-12 10:52:43

Java线程多线程

2023-06-06 08:17:52

多线程编程Thread类

2023-06-05 07:56:10

线程分配处理器

2023-04-02 17:53:10

多线程编程自测

2023-06-07 13:49:00

多线程编程C#

2011-06-10 14:13:24

关键词

2016-10-09 20:15:30

多线程多进程

2011-06-13 10:41:17

JAVA

2013-07-16 10:57:34

iOS多线程多线程概念多线程入门

2009-11-17 10:47:14

Silverlight

2013-07-16 12:13:27

iOS多线程多线程概念GCD

2009-02-24 08:36:51

多线程线程池网络服务器

2021-08-12 14:33:20

Python多线程编程

2011-07-22 14:55:20

多线程
点赞
收藏

51CTO技术栈公众号