简介
C++ REST SDK
中提供了PPLX
用来支持异步操作,姑且可以认为PPLX
是微软PPL-并行模式库/并发运行时的任务并行task
部分。
在C++ REST SDK
中,对于task
的使用也是推荐开发者查阅并发运行时
部分,在任务并行(并发运行时)可以对其进行了解。
在Programming with Tasks中描述了C++ REST SDK
中对任务编程的基本信息。
目标
在以下内容中,将了解PPL的任务部分,也就是task的使用方法,由此了解异步操作如何实现:
- 任务的创建及运行
- 任务的合并
- 任务的取消
- 处理异常
- 其它
任务的创建及运行
task构造时,构造参数可以为可调用对象或者task_completion_event
,譬如:
//构造函数
auto task = pplx::task<int>([](){
return 10;
});
//lambda
auto task = []()->pplx::task<int>{
return pplx::task_from_result(10);
};
//create_task
auto task = pplx::create_task([](){
return 10;
});
//create_task的另一种方式
pplx::task_completion_event<int> tce;
auto task = pplx::create_task(tce);
添加延续任务使用task.then
形成异步工作链:
auto s = std::make_shared<std::string>("Value 1");
return pplx::create_task([s]{
std::cout <<"Current Valus:" << *s << std::endl;
*s = "Value 2";
//异步任务1
}).then([s]{
std::cout << "Current Valus:" << *s << std::endl;
*s = "Value 3";
return *s;
//异步任务2
}).then(异步任务3)....
上述示例会依次输出Value 1,Value2,Value3...
。
使用task.get
或者task.wait
执行异步任务:
-
get
阻塞直到任务执行完成,并返回任务结果,当任务取消时,抛出task_canceled
异常,发生其它异常也会被抛出; -
wait
等待任务到达终止状态,然后返回任务状态:completed、canceled,如果发生异常会被抛出。
任务的合并
when_all 或者 &&
返回合并任务,只有当所有任务都完成时合并任务才会返回成功;如果任何一个任务被取消或者抛出异常,则合并任务会完成并处理取消状态,在合并任务get
或者wait
时抛出异常,示例如下:
std::array<pplx::task<void>, 3> tasks = {
pplx::create_task([] { std::cout << "Hello from taskA." << std::endl; }),
pplx::create_task([] { std::cout << "Hello from taskB." << std::endl; }),
pplx::create_task([] { std::cout << "Hello from taskC." << std::endl; })
};
auto joinTask = pplx::when_all(std::begin(tasks),std::end(tasks));
std::cout<<"Hello from the joining thread."<<std::endl;
joinTask.wait();//等待任务完成
如果任务类型为task<T>
,则合并任务类型为task<vector<T>>
。
when_any 或者 ||
返回合并任务,当任何任务完成时合并任务就会返回成功;如果所有任务都被取消或者抛出异常,则合并任务会完成并处理取消状态,并且如果任何任务发生异常,在合并任务get
或者wait
时抛出异常。
合并任务返回类型为task<size_t>
,即返回完成任务的索引。
任务的取消
pplx::cancellation_token_source
中包裹了cancellation_token
,用来提供取消操作,通过cancellation_token.is_canceled()
在执行任务的过程中判断任务是否要被取消:
pplx::cancellation_token_source cts;
std::cout<<"Creating task..."<<std::endl;
auto task = pplx::create_task([cts]{
bool moreToDo = true;
while (moreToDo)
{
if (cts.get_token().is_canceled())//判定是否被取消了
{
return;
}
else
{
moreToDo = []()->bool {
std::cout << "Performing work..." << std::endl;
pplx::wait(250);
return true;
}();
}
}
});
pplx::wait(1000);
std::cout<<"Canceling task..."<<std::endl;
cts.cancel();
std::cout<<"Waiting for task to complete..." <<std::endl;
task.wait();
std::cout<<"Done."<<std::endl;
注意cancellation_token_source
要使用传值操作,其类似于智能指针。
当要在异步工作链中支持取消时,需要将cancellation_token
作为构造task
的参数传递,然后结合task.wait
判断是否要取消:
pplx::cancellation_token_source cts;
auto task = pplx::task<void>([cts](){
std::cout<<"Cancel continue_task"<<std::endl;
cts.cancel();
}).then([](){
std::cout<<"this will not run"<<std::endl;
},cts.get_token());
try
{
if (task.wait() == pplx::task_status::canceled)
{
std::cout<<"taks has been canceled"<<std::endl;
}
else
{
task.get();
}
}
catch (const std::exception& e)
{
std::cout <<"exception: "<<e.what()<<std::endl;
}
处理异常
之前说过如果任务发生异常,会在get
或者wait
时抛出,但是如果希望在异步任务链中判定之前执行是否发生异常做出操作时,可以采用另外的方式。
当使用task.then
时一般是这样写的:
task<T>.then([](T t){
//处理任务结果t
})
这时候进入then时之前的任务已经执行完成了,task.then
有另外一种写法,能够在then
时并没有执行任务:
task<T>.then([](task<T> task){
try
{
task.get(); //使用get或者wait执行任务
}
catch(...)
{
//处理异常
}
})
其它
- task_from_result
从结果直接构造task,用来不再继续执行,返回task<TResult>
结果 - task_from_execption
从异常构造task - task_options
task创建选项,用来指定task的调度器、取消标志等内容
总结
pplx
为我们提供了基于任务的编程模型,为异步编程提供了相对简单的实现方法,后续在C++ REST SDK
使用中可以看到其应用。
网友评论