【Windows8开发】异步编程进阶篇之 create_async, create_task, make_task区别与联系

时间:2021-08-25 19:01:20
create_async, create_task, make_task都是WinRT下为异步编程服务的API,它们之间有何联系,又有何区别呢?
create_async是为跨语言调用服务的,也就是说如果希望封装的方法可以被C++以外语言的组件调用,那就需要create_async来创建异步处理,那除了这点外,它与create_task在实际应用中还有什么区别与联系呢?首先是返回值。create_async基于跨语言的考虑返回4种支持个语言平台间通用的异步处理接口:1. Windows::Foundation::IAsyncAction : 无返回值,无处理进度报告
2. Windows::Foundation::IAsyncActionWithProgress<TProgress>:无返回值,有处理进度报告
3. Windows::Foundation::IAsyncOperation<TResult>:有返回值,无处理进度报告
4. Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>:有返回值,有处理进度报告

而create_task则返回Concurrency::task<TRESULT>对象,看如下示例:
task<int> op1 = create_task([]()
{
return 0;
});

IAsyncOperation<int>^ op2 = create_async([]()
{
return 0;
});

两者都可以使用auto关键字来处理返回值
auto op1 = create_task([]()
{
return 0;
});
auto op2 = create_async([]()
{
return 0;
});

Concurrency::task可以很方便的使用then方法来处理异步处理间的延续异步任务调用(task continuation),如上面的处理中,可以如下使用then方法:
op1.then([](int v){
return 0;
});

那如果create_async创建的异步处理在C++中如何实现task continuation?很简单,其实create_task的参数可以是IAsyncAction等4种异步处理接口,所以结合create_task就可以这样来使用:
create_task(op2).then([](int v){
return 0;
});

那如果是在C++以外的语言中调用create_async封装的C++方法,如何实现then类似的功能呢?因为这不是本文讨论的重点,只简单说明下,C#中可以通过async和await关键字来控制;而js中也通过了类似的then方法来实现,请参考【windows8开发】C++开发WinRT组件和JS调用一文。create_task是WinRT提供的一个便于创建task的API,其实通过直接构建task<TRESULT>对象也可以实现一样的目的:
task<int> op1([]()
{
return 0;
});

既然说到了task和then,顺便介绍个概念,Value-Based和Task-Based。看下面的例子:
create_task([]()
{
return 0;
}).then([](int x)
{
return x;
});

首先要明白第一个Lambda表达式的return值会作为第二个then中Lambda表达式匿名函数的参数。而如上这种写法是Value-Based的。那Task-Based呢?写法上有一点区别,就是第二个Lambda表达式的参数变成了task<int>类型。
create_task([]()
{
return 0;
}).then([](task<int> pretask)
{
return pretask.get();
});

那实际功能上的区别呢?当你希望前任务发生异常或某些情况下被cancel时,后序任务仍旧能够正常执行,那就应该使用Task-Based的形式,而反之,则使用Value-Based,后序的任务将不会被执行。当然期间会涉及到一些处理细节,会在后续介绍异步编程任务取消机制、异常处理等概念时再详细说明。
最后再来说说make_task。其实make_task跟前两者没有直接的联系,它是用来创建某个任务组(task group)中的单个任务的,在使用concurrency::structured_task_group时,会通过make_task来创建每个任务的任务句柄(task handle),然后再把这些任务句柄添加到任务组中。看个make_task的简单例子:
// Create a task group that serves as the root of the tree.
structured_task_group tgroup;

// Create a child task.
auto task1 = make_task([&] {
// TODO: Perform work here.
});

// Create a child task.
auto task2 = make_task([&] {
// TODO: Perform work here.
});

// Run the child tasks and wait for them to finish.
tgroup.run(task1);
tgroup.run(task2);
tgroup.wait();

在后续介绍任务组的部分会再详细说明make_task,这里只是简单提一下,知道它跟create_task完全不是一回事就可以了,