在上一篇文章中,我介绍一些在VS中C++异步编程的简单概念和语法。这篇,我们讨论一下异步编程中取消操作的概念。
取消操作:
取消一个正在进行的task,方式大概分两种,一种是从内部取消,另外一种是从外部取消。
我们通过cancel_current_task 去从内部取消这个task
#include
#include
#include
#include
using namespace Concurrency;
using namespace std;
void do_work()
{
// Simulate work.
wcout << L"Performing work..." << endl;
wait(250);
}
int _tmain(int argc, _TCHAR* argv[])
{
wcout << L"Creating task..." << endl;
task<void> t([]() {
bool moreToDo = true;
int i=0;
while (moreToDo)
{
i++;
if(i==4)
{
wcout<"In cancellation"<// call this function to cancel
cancel_current_task();
}
do_work();
}
});
wait(1000);
}
这里要说明一点的是,在我们声明这个task之后,我们并没有调用.wait() 或者.get() 去执行他。只是调用了wait(1000);这个task就被执行了。这个是因为,wait(1000)这个函数,表面上是让当前的线程休息1000毫秒,但是他会激发后台scheduled状态的task,当这个task声明之后,他的状态就是scheduled,当我们在主线程调用wait()的方法,之前声明的task就会被激发和执行,所以我们不需要调用.wait()或者.get()去执行他。
如果我们想从外部取消这个task,我们需要传一个token给这个task,然后我们通过 这个方法去取消这个task
wcout << L"Creating task..." << endl;
cancellation_token_source cts;
auto token=cts.get_token();
task<void> t([]() {
bool moreToDo = true;
while (moreToDo)
{
// Simulate work.
do_work();
}
},token);
wait(1000);
cts.cancel();
// Wait for the task to cancel.
wcout << L"Waiting for task to complete..." << endl;
t.wait();
wcout << L"Done." << endl;
return 0;
在外部取消这个task的过程中,我们想在内部获得取消这个动作的信号。我们可以通过 函数去获取
task<
void> t([]() {
bool moreToDo =
true;
while (moreToDo)
{
// Check for cancellation.
if (is_task_cancellation_requested()) {
wcout << L
"Get the cancel event" << endl;
// Cancel the current task.
cancel_current_task();
// You must end this task in front line or in this line
moreToDo =
false;
}
else {
// Perform work.
do_work();
}
}
}, token);
// Wait for one second and then cancel the task.
wait(
1000);
wcout << L
"Canceling task..." << endl;
cts.cancel();
// Wait for the task to cancel.
wcout << L
"Waiting for task to complete..." << endl;
t.wait();
这里要注意的是, 当我们调用cts.cancel()的时候, 返回true,但是这个task并没有被取消,我们必须要在task里面手动调用cancel_current_task,或者通过其他办法让函数结束。如果我们不判断 就不需要我们在task内部手动取消。
我们还可以注入callback 函数,在我们尝试外部取消的时候。
cancellation_token_source cts;
auto token=cts.get_token();
cancellation_token_registration cookie;
cookie=token.register_callback([](){ wcout << L
"In cancellation callback..." << endl;});
task<
void> t([]() {
bool moreToDo =
true;
while (moreToDo)
{
// Simulate work.
do_work();
}
},token);
wait(
1000);
cts.cancel();
token.deregister_callback(cookie);
以上的例子都是基于while循环的,我认为这样可以便于理解。同样,我们也可以用event,在Concurrency namespace里面,系统事件已经被封装成event Class (Concurrency Runtime) 这个类,有set wait 等方法便于使用。下面的例子就是基于event 和callback函数的
// task-cancellation-callback.cpp
// compile with: /EHsc
#include
#include
using namespace Concurrency;
using namespace std;
int wmain()
{
cancellation_token_source cts;
auto token = cts.get_token();
// An event that is set in the cancellation callback.
event e;
cancellation_token_registration cookie;
cookie = token.register_callback([&e, token, &cookie]() {
wcout << L"In cancellation callback..." << endl;
e.set();
// Although not required, demonstrate how to unregister
// the callback.
token.deregister_callback(cookie);
});
wcout << L"Creating task..." << endl;
// Create a task that waits to be canceled.
task<void> t([&e]() {
e.wait();
}, token);
// Cancel the task.
wcout << L"Canceling task..." << endl;
cts.cancel();
// Wait for the task to cancel.
t.wait();
wcout << L"Done." << endl;
}
这里还要注意deregister_callback,传入参数必须是引用类型。
最后关于取消操作,还要说一点就是如何给一个parallel_for 添加取消动作,首先,这个函数本是是没有支持取消操作的,我们要给他外面套一层task,通过取消外面的task,来取消里面的parallel_for。
int wmain()
{
cancellation_token_source cts;
//
// Run a parallel_for loop in a call to run_with_cancellation_token.
wcout << L
"Running a task with a cancellation token..." << endl;
run_with_cancellation_token([cts] {
// For illustration, cancel the overall operation on the third
// iteration of a parallel loop. The parallel_for call implicitly
// inherits the cancellation token of the parent task.
long counter =
0;
parallel_for(
0,
100, [cts, &counter](
int n) {
if (InterlockedIncrement(&counter) ==
3)
{
wstringstream ss;
ss << L
"Canceling..." << endl;
wcout << ss.str();
cts.cancel();
}
wstringstream ss;
ss << L
"In iteration " << n << L
" of parallel_for..." << endl;
wcout << ss.str();
});
}, cts.get_token());
wcout << L
"Done." << endl;
}