std::future和std::promise

并行开发挺复杂的,特别是在试图用好线程和锁的过程中。如果要用到条件变量或std-atomics(一种无锁开发方式),那就更复杂了。C++0x提供了future和promise来简化任务线程间的返回值操作;同时为启动任务线程提供了packaged_task以方便操作。其中的关键点是允许2个任务间使用无(显式)锁的方式进行值传递;标准库帮你高效的做好这些了。基本思路很简单:当一个任务需要向父线程(启动它的线程)返回值时,它把这个值放到promise中。之后,这个返回值会出现在和此promise关联的future中。于是父线程就能读到返回值。更简单点的方法,参看async()。

标准库中提供了3种future:普通future和为复杂场合使用的shared_future和atomic_future。在本主题中,只展示了普通future,它已经完全够用了。如果我们有一个future

f,通过get()可以获得它的值:

X v = f.get();  // if necessary wait for the value to get computed

如果它的返回值还没有到达,调用线程会进行阻塞等待。要是等啊等啊,等到花儿也谢了的话,get()会抛出异常的(从标准库或等待的线程那个线程中抛出)。

如果我们不需要等待返回值(非阻塞方式),可以简单询问一下future,看返回值是否已经到达:

if (f.wait_for(0))
{   
    // there is a value to get()                
    // do something        
}        
else
{                
    // do something else       
}

但是,future最主要的目的还是提供一个简单的获取返回值的方法:get()。

promise的主要目的是提供一个”put”(或”get”,随你)操作,以和future的get()对应。future和promise的名字是有历史来历的,是一个双关语。感觉有点别扭?请别怪我。

promise为future传递的结果类型有2种:传一个普通值或者抛出一个异常

try {
        X res;
        // compute a value for res
        p.set_value(res);
}
catch (…) {   // oops: couldn’t compute res
        p.set_exception(std::current_exception());
}

到目前为止还不错,不过我们如何匹配future/promise对呢?一个在我的线程,另一个在别的啥线程中吗?是这样:既然future和promise可以被到处移动(不是拷贝),那么可能性就挺多的。最普遍的情况是父子线程配对形式,父线程用future获取子线程promise返回的值。在这种情况下,使用async()是很优雅的方法。

packaged_task提供了启动任务线程的简单方法。特别是它处理好了future和promise的关联关系,同时提供了包装代码以保证返回值/异常可以放到promise中,示例代码:

void comp(vector& v)
{
        // package the tasks:
        // (the task here is the standard
        //  accumulate() for an array of doubles):
        packaged_task pt0{std::accumulate};
        packaged_task pt1{std::accumulate};

        auto f0 = pt0.get_future();     // get hold of the futures
        auto f1 = pt1.get_future();

        pt0(&v[0],&v[v.size()/2],0);    // start the threads
        pt1(&[v.size()/2],&v[size()],0);

        return f0.get()+f1.get();       // get the results
}

参看:

(翻译:interma)