先来看一下和.NET Task基本等价的task类型。这也是微软C++扩展中并发异步线程管理的核心类型之一。微软围绕concurrency::task的设计的一些方法与C#中的Task相关方法真的非常下。下面的表格对比了C#的Task与C++中的concurrency::task。有C# Task基础的话,对于concurrency::task很容易就能上手。
C# Task C++ concurrency::task
构造 方式1 constructor constructor
构造 方式2 Task.Factory.StartNew()
用于异步 - create_task()
构造 方式3
用于并行 - make_task()
返回task_handle,和task_group等同用。
阻塞 - 等待完成 task.Wait() task::wait()
阻塞 - 等待获取结果 GetAwaiter().GetResult() task::get()
任务状态类型 TaskStatus concurrency::task_status
并行 - 等待全部 Task.WhenAll() concurrency::when_all
并行 - 等待部分 Task.WhenAny() concurrency::when_any
异步 - 任务延续 Task.ContinueWith() task::then()
接着讨论一下本节的重点内容,微软给C++带来的异步支持。
普通异步
看过之前介绍C#异步的部分,可以知道支持异步的系统无非就由以下以下几部分组成:任务创建、任务延续、任务等待、取消、进度报告等。依次来看一下ppltask.h中支持这些部分的方法。
create_task方法可以将函数对象(广义上的函数对象包含如lambda表达式,在C++11中也多用lambda表达式作为函数对象)包装成task类对象。如上文所述,定义在ppltask.h中,位于concurrency命名空间下的task类和异步方法关系最密切。下面的代码示例了concurrency::task的创建。
task<int> op1 = create_task([]() { return 0; });在C++11中一般都使用auto直接表示一些复杂的类型,让编译器去推断。例子中写出完整的类型可以让读者更好的理解方法的返回类型。
而类似于.NET Task中的ContinueWith方法的task::then方法,基本使用如下:
op1.then([](int v){ return 0; });在C++中由于没有类似C#中async/await关键字的支持,所以后续任务不能像C#中那样直接跟在await ...语句后,必须通过task::then方法来设置。
then方法也可以实现链式调用,如:
auto t = create_task([]() { //do something }).then([](int v){ return 0; });关于后续代码执行上下文的问题,如果create_task方法接受的函数对象返回的是task<T>或task<void>则后续代码会在相同的线程上下文运行,如果返回的是T或void则后续任务会在任意上下文运行。可以使用concurrency::task_continuation_context来更改这个设置。具体用法是将task_continuation_context传给task::then其中那些接受task_continuation_context类型参数的重载。如果参数值为concurrency::task_continuation_context::use_arbitrary,则表示指定延续在后台线程上运行,如果参数值为concurrency::task_continuation_context::use_current,则表示指定延续在调用了task::then的线程上运行。如:
auto t = create_task([]() { //do something }).then([](int v){ //do something else; },task_continuation_context::use_arbitrary());//then()中传入的代码将在后台线程执行,相对于C#中配置ConfigAwait(false)。
对于取消和异步的支持,将在下一小段进行介绍,那里的实现方式同样可以应用到这一部分中。
使用create_task的方式创建task的方法只用于C++内部对task的管理。如果是希望将异步作为WinRT组件发布需要使用下面介绍的create_async。
如果是纯C++中处理多线程任务,除了使用Windows中所提供的task,还可以考虑C++11标准库中的thread,后者跨平台更好。后文会有一部分介绍C++11的thread。如果是对C#的TPL模型很熟悉,转到C++使用ppltask.h中的task会发现模型一致性很高。
支持WinRT的异步
1. 提供WinRT标准的异步方法
通过create_async方法可以将函数转为异步函数,即这个方法是返回IAsyncInfo对象的。通过这个方法可以将代码包装成WinRT中标准的异步方法供其它语言调用。被包装的代码一般是可调用对象,在C++11中一般都使用Lambda表达式。返回的IAsyncInfo的具体类型(上文介绍的四种之一)是有传入的参数决定的。
create_async的声明:
template<typename _Function> __declspec( noinline ) auto create_async(const _Function& _Func) -> decltype(ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func));可以看到为了确定这个模板方法的返回类型使用了C++11的decltype和位置返回类型等新特性。
通常情况下,传入create_async的函数对象的方法体是一般的代码。还以把create_task方法的调用传入create_async接收的lambda表达式的方法体中,create_task返回的concurrency::task也可以配置一系列的then(),最终这些配置都将反应给最外部的create_async的包装。