首页 > 应用开发 > Java > 从Win8回顾微软平台的各种技术
我安装好 Win8 CTP 后做的第一件事情就是用调试器研究 Win8 各个组件的协作关系 . 从我半天的研究结果看来 , Win8 真是一个让我爱不释手的产品 . Win8 里面涉及到的很多技术正好也是我的兴趣所在 . 这篇文章简单回顾一下这些技术的变迁 , 优缺点 , 和对 Win8 的影响 .
注意 , 下面提到的对 Win8 的分析 , 是基于公开的 Win8 CTP 来做的 . 相信 Win8 面世的时候 , 这些技术和细节 , 都会发生重大改变 . 所以这篇文章不具备实践上的指导价值 .
COM -Component Object Model 通用组件模型
COM 是上个世纪中期设计出来的伟大产品 . COM 旨在解决软件复用的问题 . 在 COM 以前 , 大家都是用代码级别的复用 , 常见的就是 C/C++ 的库 , 无论是原代码库还是 lib 库 , 都是需要编译后才能重用的 . COM 使得技术人员可以在二进制上进行复用 . 从 Win95, OLE32 和 Office95 系列开始 , COM 就是微软平台上的一个技术基石 , 无论是 DirectX API, 还是最常见的剪贴板 , 以及后来 .NET Framework 的 host 接口 , 都离不开 COM. 但任何伟大的产品 , 都有局限的一面 . COM 在局限性在下面一些地方
STA/MTA/NTA 等等线程模型过于复杂
线程模型 , 特别是 STA, 设计的目的是方便使用者 . 但 COM 的线程模型严重依赖于太多系统组件 , 比如 Win32 Message, RPC 和 Windows 系统服务 , 使得程序员需要熟悉和了解太多系统知识才可以正确地使用线程模型 . 否则用 STA 导致死锁简直就是家常便饭 .
开发工具没有提供足够支持
COM 和 Visual Studio 6.0 的关系 , 就如同现在 CLR2/VS2005, CLR3.5/VS2008 和 CLR4/VS2010 的关系一样铁 . 使用 COM 开发 , 当时的选择要么是 VB6, 要么用 ATL. 这两者都有天生的局限 . VB6 适合企业开发 , 特别是当时流行的 MIS 系统 , 数据库系统这样的 CS 应用 , 但是 VB6 不够灵活 . 而且 VB6 里面由于缺少多线程支持 , 无法用 MTA 的模型 . ATL 功能强大 , 足够灵活 , 但是使用起来特别复杂 . 每次实现一个接口 , 都要做一大堆 C++ 的仪式性工作 , 比如实现多重继承 , 定义新的模板 , 使用大量的 C 宏 . 神经再粗大的程序员 , 都经不起这样用 C++ 的 .
无止境地扩充到 DCOM, COM+, DTC, MSMQ 以及后来的 .NET Remoting/WCF, 使得最后的复杂度无法控制
为了更好地适应企业级别的开发 , COM 被进一步延伸和演化成了 DCOM 和 COM+. 所谓 " 企业级别 ", 其实是指对安全性和可伸缩性的更高要求 . 经典的 COM 是一个进程内模型 , 无法让不同的代码运行在不同的帐号下的 , 因为同一个进程只能启在唯一的帐号下 . 虽然通过 impersonate 等方法可以适当地解决问题 , 但是为了让安全模型更为全面 , 正确的做法是让不同的接口实现 , 能够跨越进程甚至跨越机器等安全边界运行 , 要能够赋予不同的接口不同的安全级别 , 能够和域帐号集成 , 支持不同等级的加密等等 . 远程通用组件模型 , 也就是 DCOM 就这样诞生了 . 读者可以尝试运行以下 dcomcnfg.exe 这个工具 , 展开一些节点 , 看看属性页 , 就能体会到 DCOM 的功能是多么让人眼花缭乱了 . 对于可伸缩性 , 微软更上一层楼 , 在 DCOM 的基础上加入了对象池和新的同步模型做成了 COM+. 风靡十年的 ASP, 就是运行在 COM+ 框架下的最好例子 . 更让人叹为观止的是 , 微软把 DTC 和 MSMQ 的设计也和 COM+ 模型绑定起来 . 陌生的读者可能不了解 DTC 是个多么 nb 的东西 . 简单说 DTC 是可以让程序员一行代码都不用写 , 就让 SQL Server 和 Oracle 的数据库操作运行在同一个事务边界里面 . 可以想象 , 在 MSSQL Server 刚进入市场的时候 , 微软推出这样的功能 , 对于抢占 Oracle 的市场份额有多么重要 . DTC 和 COM+ 当时成为了很多大公司的标准配置 , 以至于后来设计 .NET Remoting 和 WCF 的时候 , 都还是要考虑对 DTC 的支持 . 这些强大而且复杂的功能 , 让本来就复杂的 COM 更加恐怖 . 这样的复杂度虽然也体现了当时的市场需求 , 但由于缺乏配套的开发工具 , 新的开发语言和更优美的抽象 , 使得这条路最后越走越窄 .
.NET Framework/CLR
在我眼中 , CLR 的各方面简直是无可挑剔的 . 但可能正是因为 CLR 太好了 , 让微软从 2003 年开始 , 对 unmamanged world 的投资就不大了 .
为了争取企业客户 , 全力推广 CLR 是最正确的做法 . 毕竟绝大多数的程序员 , 一辈子都是和数据库 , 和 UI 代码打交道 . 你总不能让他们一辈子都用 C 去管理内容 , 创建窗口句柄吧 . CLR 的性能也很有竞争力 , 与之对应的编程语言和开发工具也非常给力 , 每个新版本都带来长足进步 . 但是 , 这些再好 , 也无法掩饰一个悖论 : 要用用 C# 写出性能可以和 C++ 一个数量级的程序 , 不是不可能 , 而是花费的代价往往比直接用 C++ 写更大 . 这里面有很多原因 . 比如系统最底层的 API 还是 unmanaged 的 , 通过 CLR 作 interop 的性能损失无法忽略 . 比如用 C# 的话程序员的控制力很弱 , 从工具和语言层面上很难对性能精雕细作 , 一不注意就 box/unbox 了 . 比如 JIT 编译器为了实现自己的安全模型和异常处理 , 每次访问成员函数的都是都要生成代码确认 this 指针是否为空 . 这个世界上还是有很多程序 , 是需要对性能做严格控制的 . 在 CLR 高速发展的几年中 , 对应的系统平台 , Win32 API, C++ 开发工具 , 只有完善性的改善 , 并没有重大突破 . 这个问题对 Windows 平台本身影响不大 , 但如果寄希望于在移动设备上 , 大家都指着 C# 来开发 , 就有点天方夜谭了 . 这样的失衡 , 使得微软在移动设备和消费者产品这两个严重需要 unmanaged 和系统投入的领域缓慢发展了很长时间 .
WPF
在我看来 , WPF 是一个设计得很美的产品 . WPF 解决了传统 Win32 UI 程序的四大局限 . 1) Win32 的绘图是由各自 Window 元素独立控制 , 基于 GDI 的 . WPF 引入了 rendering thread 来提高性能 , 优化算法 , 借用 GPU 加速 . 2) Win32 依赖于 GDI Object, 在开发复杂窗口程序的时候 , 很容易就遭遇资源泄露和资源不足 . 比如早期的淘宝旺旺 , 开到几十个窗口的时候 , 程序就会出问题 . 所以淘宝针对这个问题 , 使用了统一控制台 , 合并多个窗口到标签页的方法来解决 . 而 WPF 只有最外面的窗口使用了 Win32 Window 和 GDI, 内部的元素都是抽象成了 WPF 自己的元素 , 不额外占用 Win32 GDI 资源的 . 3) Win32 窗体程序严重依赖 Windows Message 模型 . 这要求程序员对系统知识有深入的了解 . 而且 Win32 API 并不是非常利于使用 , 比如要进行 UI thread 和 Worker thread 之间的通信 , 往往需要和 SendMessage 这样的 API 打交道 . 在 WPF 中 , 引入了 Dispatcher 类和 BeginInvoke 方法 , 把这些复杂问题抽象了 . 加上 CLR 提供了更方便高效的开发环境 , 使用 WPF 是很愉快的工作 . 4) Win32 缺乏数据 , 设计和代码三者之间的模式抽象 . 这三者在 WPF 中对应了数据绑定 , XAML 文件 , 以及后台代码 . 在 WPF 中可以更直观地使用各种模式比如 MVC 和 MVVM. 这些都体现了 WPF 设计上的优美 .
再优美的东西都还是有局限性的 . WPF 的问题在于过多的模式和对 CLR 过度的依赖 . 了解 WPF 框架的人都知道 , 就一个简单的 dependent property, 就把设计模式这本书里面的模式用掉一大半了 . 分析 WPF 框架代码的话 , 简直就是看一本设计模式的百科全书 . 我曾经统计过 , 关于 mouse click 这样一个 event 回调 , WPF 里面有 7 种不同的实现方法 , 分别各有好处 , 旨在解决不同问题 . 在这样高度灵活的背后 , 牺牲的是程序性能 . 无论是五花八门的模式 , 还是最常用的数据绑定 , 背后的主力都是 CLR 的 reflection. 过度依赖于 reflection 导致 WPF 程序规模一大 , 性能上就出问题 . 就算再怎么优化 , 也总找不到原生 Win32 程序那般流利的感脚 . 使用 reflection 也体现了对 CLR 的依赖 . 所以前面 CLR 的局限性 , 也适用于 WPF.
微软产品的互操作性
微软的产品线虽又长又多 , 但是各个产品之间一定是能够互操作的 . 比如 C# 可以和 C++ 互相调用 . 任何语言开发的程序都可以嵌入 Browser Control 来借用 IE 的功能 . Office 暴露了 VBA 接口 , 通过 VBScript 都能够自动化 Office 程序 . 各种管理工具无论是 Explorer 还是 MMC, 都暴露了编程接口可以让程序员添加自己的功能 . 我见过客户用 ASP.NET 在后台用 VBScript 生成 Excel 表格 , 然后把表格嵌入在 IE 浏览器中 , 再使用 JavaScript 来帮助客户编辑 , 最后提交回 MSSQL 数据库做完成报销功能的 . 完善的互操作性充分保护了用户的投资 , 使得客户对微软平台用一次就上瘾 , 几乎没有不可能完成的任务 . 仔细分析 , 其实这些互操作有一个共性 , 都是把暴露 COM 接口作为内部实现原理 . 这个做法导致了三个局限性 , 首先是牵涉到了前面提到的 COM 的复杂度 . 其次是潜在的性能损耗 . 最后是在具体开发的时候 , 都需要一些仪式性的工作来引入或者定义 COM 接口 , 使得开发过程不够自然流畅 . 在 CLR 和 COM 互操作的调用栈里面 , CLR 的 RCW, CCW, 安全处理 , 列集拷贝等等 , 耗费的时间带来的性能开销简直是可以到了肉眼可察觉的地步 ( 听硬盘的声音和看任务管理器里面 CPU 的波动 ).
Win8
在讨论完这些技术背后的故事后 , 再看看为啥我就对 Win8 爱不释手了 .
Win8 引入了 Windows Runtime, 简称 WinRT. WinRT 是一个操作系统模块 , 运行在用户态 , 介于 Win32 的上层和应用程序的下层 , 目的在于提供更高效友好的开发接口供 Win8 的程序员使用 . WinRT 在二进制模型上基本就是照搬了经典的 COM. WinRT 和 CLR 互不依赖 , WinRT 可以被 CLR 使用 . WinRT 通过 C/C++ 实现 , 效率高是一个方面 , 更重要的时 Win8 引入了 projection 的概念 , 就是可以把 WinRT 的 API 用最直接最高效的方法 , 提供给上层的编程语言调用 . 这个语言可以是 C#, C 或者 JavaScript.
对于第一次接触 Projection 的朋友 , 可以把 Projection 认为是一种新的 Windows API 模型 . 传统的操作系统 API, 要么是暴露 DLL 的方法 , 要么是通过 COM 接口 . 无论是哪一种 , 在 CLR 中调用的时候都有不小的开销 . 使用这些传统 API 的效率 , 比调用一个 C# 自己的方法 , 效率差了多个数量级 , 根本的原因在于 CLR 的安全模型 , 内存模型和传统的 unmanaged 模型不兼容 , 所以跨越边界的调用需要额外的代码来处理 . 而 Projection 提供的模型 , 是在提供新功能的同时 , 还针不同编程模型和语言 , 提供了最利于它们调用的方法 . 这样就主动避免了不同模型之间为了互相兼容导致的开销 , 也使得程序员写代码的时候非常自然流畅 , 调用的时候根本感觉不到和调用本地函数的区别 . 当然 , 能够实现这一点 , 也是得益于 CLR, C# 语言和 VS 开发工具这十年的长足发展 . 举个例子 , C# 5.0 中引入了 await 关键字 , WinRT 中引入了 async operation. Projection 技术把 C# 中的 await 语句转换为 WinRT async operation 的调用 , 而且这个调用直接从 managed code 直接跳到 unmanaged code, 中间没有任何冗余 , 也不需要 CLR Engine 的介入 . 进一步的信息 , 可以参考 Build 大会关于 WinRT 的多个演讲 . 后面的 callstack 也提供了直观的例子 .
前面提到了 COM 的局限性在于一个轻量的二进制模型 , 被硬生生的扩展成一个无所不能的框架 . WinRT 取其精华 , 去其糟粕 , 借用了 COM 的轻便 , 舍弃了复杂性 , 在扩展性上依托于上层的编程语言和工具 . WinRT 通过 projection 的技术解决了传统互操作性效率不高使用不方便的问题 . 以前的路线是希望所有的产品和技术最后都统一到 CLR 上来 . 现在修正为底层模型通过 WinRT 和 C 来实现 , 然后把这一层高效的组件无缝提供给上层的开发技术比如 CLR 来使用 . 这个转变重新重视 unmanaged 层面的二进制模型 . 归纳为 , unmanaged 模型的优势在于执行效率高 , 可以通吃所有场景 , 缺点在于开发和使用成本也高 . CLR 的优势在于开发成本低 , 缺点在于无法通吃各种需求 . 现在微软自己用 unmanaged 来做 WinRT, 然后把 WinRT 提供给上层语言 , 这两者就可以取长补短了 .
有了 WinRT, 有了 unmanaged 的回归 , 再加上微软开发工具和 C# 语言的长足发展 , 前面介绍的各种技术在 Win8 里面就相得益彰了 . Metro 是如何修复 WPF 的缺陷就显而易见了 : Win8 的 metro 程序继续使用 WPF 中引入的 rendering 模型和 XAML, 但是在 control 的基础设计和实现上 , 从 CLR 转移到了 C, 然后通过 WinRT 来暴露给使用者 . 至于使用的灵活性 , 比如要不要实现数据绑定 , 就看上层使用者自己的选择了 .
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70020836/viewspace-2910061/ ,如需转载,请注明出处,否则将追究法律责任。