从以前的windows应用程序开发转变到Windows8 Metro App开发,如果非要说最需要改变的观念有哪些,显而易见的Metro UI风格大家肯定都认同,而另外一个就是异步处理。从win32早期几乎全盘的同步API,到后来.Net开始支持异步API,微软其实已经做出了API风格上的转变,但是晦涩的回调处理,异常,调试难度让大多数开发者对异步模型开发望而却步,同步处理的观念紧锁住了几乎绝大部分开发者的思想,试问在某些库宣称同时支持同步API和异步API后,包括我自己,又有多少开发者选择去尝试用异步API来构筑他们的应用程序呢?
如果有公司宣称,“在我的平台上开发App,只要你的应用程序在UI线程上运行超过50ms就会被禁止,你必须把50ms以上的阻塞处理都搬到后台处理。”你可能觉得发表这样言论的人在做梦!但是,这正是windows8告诉我们开发Metro App必须遵循的规则。个人觉得,这个定义虽然会让我们在开发中多少感觉到一点别扭,但却会把windows程序UI操作的用户体验推上一个台阶。
进入正题,关于win8异步模型,让我们从Concurrency::task这个C++中支持异步API的类开始。
先看一个例子:
#include <ppltasks.h> #include <iostream> using namespace Concurrency; using namespace std; int main() { task<int> t([]() { return 10; }); t.wait(); wcout << t.get() << endl; return 0; }
输出 10
先解释下这段程序做了什么,task<int>定义了一个并行任务并设定会返回int型值,t的初始化用到了Lambda表达式,其中就涉及到一个异步处理(return 10),wait()会等待异步处理结束,从get()方法能得到异步处理的返回值。这里主要用到了Concurrency::task这个类。
在异步处理中,经常希望在一个异步操作结束后唤起另外一个异步处理。Concurrency::task也支持这种功能。
#include <ppltasks.h> #include <iostream> using namespace Concurrency; using namespace std; int main() { task<int> t([]() { return 10; }); auto ta = t.then([](int n){ return n + 10;}); wcout << ta.get() << endl; return 0; }
输出 20
这里通过then()方法又加了一个异步处理,第一个return返回的10会作为第二个异步处理Lambda匿名函数的参数,get()方法得到n+10的结果。在这里then方法返回的还是task<int>对象,所以如果后续还要追加其他任务可以这样写:
t.then([]{}).then([]{}).then([]{})..........
这里通过then()方法追加的延长任务(Continuation task)有Value-Based和Task-Based两种
// Value-Based: t.then([](int n) {}); // Task-Based: t.then([](task<int> preTask) {});
两者的区别是: Value-Based类型的延长任务在前序任务被取消或发生异常时就不会被执行,而Task-Based类型延长任务则仍旧会被执行。
怎么样,Concurrency::task的使用很简单吧。但是上面这些其实只是task类的一部分功能,还有很多其他功能,比如任务组合功能Concurrency::when_all,Concurrency::when_any,任务延时,任务取消等,本文只是作为一个引子,有兴趣的可以参考微软的MSDN,自己学习吧。
另外还想说明一点,Concurrency::task的API并不支持来自C++以外语言的调用,所以如果你想封装异步接口的组件供其他语言(JS,C#)调用,就不能使用Concurrency::task直接进行异步操作。针对这种情况Win8还提供了其他的API,我会在下一篇文章中进行说明。
其实在前面写的《【windows8开发】C++开发WinRT组件和JS调用》这篇文章中也有关于这方面的内容,JS调用C++组件的实例中
就用到了异步处理。