实现(可能是无效的)承诺

时间:2014-12-17 19:31:47

标签: c++ c++11

我正在编写一些多线程代码并使用promise / future在不同的线程上调用函数并返回其结果。为了简单起见,我将完全删除线程部分:

template <typename F>
auto blockingCall(F f) -> decltype(f()) 
{
    std::promise<decltype(f())> promise;

    // fulfill the promise
    promise.set_value(f());

    // block until we have a result
    return promise.get_future().get();
}

这适用于任何返回非void的函数。获得未来的return语句也适用于void。但如果fvoid函数,我无法履行承诺,因为:

  

promise.set_value(F()); // 错误:无效使用void表达式

是否有一些聪明的方法可以在void情况下设置内联的值,或者我是否必须编写一个像call_set_value(promise, f)这样的辅助函数,它具有std::promise<R>的重载和std::promise<void>

2 个答案:

答案 0 :(得分:13)

是。功能过载是最干净的解决方案:

set(promise, f);

然后将set实现为重载函数,如:

template<typename F, typename R>
void set(std::promise<R> & p, F && f) //handle non-void here
{
    p.set_value(f()); 
}

template<typename F>
void set(std::promise<void> & p, F && f)  //handle void here
{
    f();
    p.set_value(); 
}

答案 1 :(得分:13)

promise只是一种异步结果提供者。而不是promise你可以使用包裹可调用对象的packaged_task,类似于std::function,除了调用它使得结果可以通过future获得(当然它处理void和非空结果之间的差异:

template <typename F>
auto blockingCall(F f) -> decltype(f()) 
{
    std::packaged_task<decltype(f())()> task(std::move(f));

    task();

    // block until we have a result
    return task.get_future().get();
}

N.B。根据当前标准,如果task()task.get_future()发生在不同的线程上,那么此代码会有数据竞争(原来使用保证也是如此),因此您应该先调用get_future()将任务交给另一个线程。在实践中,它应该对实际实现是安全的,并且存在库问题(LWG 2412)以使其无论如何都有效。