C++异步编程 for VS2011(二)

在上一篇文章中,我介绍一些在VS中C++异步编程的简单概念和语法。这篇,我们讨论一下异步编程中取消操作的概念。

 

取消操作:

取消一个正在进行的task,方式大概分两种,一种是从内部取消,另外一种是从外部取消。

我们通过cancel_current_task  去从内部取消这个task

 #include <ppltasks.h>

#include <iostream>
#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 函数去获取

    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()的时候,is_task_cancellation_requested 返回true,但是这个task并没有被取消,我们必须要在task里面手动调用cancel_current_task,或者通过其他办法让函数结束。如果我们不判断is_task_cancellation_requested  就不需要我们在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 <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。

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(0100, [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

 

 

posted @ 2012-03-25 16:30  Jesse Jiang  阅读(3357)  评论(0编辑  收藏  举报