在上一篇文章中,我介紹一些在VS中C++異步編程的簡單概念和語法。這篇,我們討論一下異步編程中取消操作的概念。
取消操作:
取消一個正在進行的task,方式大概分兩種,一種是從內部取消,另外一種是從外部取消。
我們通過cancel_current_task 去從內部取消這個task
#include <ppltasks.h>
#include <array>
#include <list>
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<<L " In cancellation "<<endl;
// 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,然后我們通過 cancellation_token_source::cancel 這個方法去取消這個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的過程中,我們想在內部獲得取消這個動作的信號。我們可以通過is_task_cancellation_requested 函數去獲取
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()的時候,is_task_cancellation_requested 返回true,但是這個task並沒有被取消,我們必須要在task里面手動調用cancel_current_task,或者通過其他辦法讓函數結束。如果我們不判斷is_task_cancellation_requested 就不需要我們在task內部手動取消。
我們還可以注入callback 函數,在我們嘗試外部取消的時候。
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 <ppltasks.h>
#include <iostream>
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。
{
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;
}
引用自:http://msdn.microsoft.com/en-us/library/windows/apps/dd984117(v=vs.110).aspx