什么是std :: promise?

本文翻译自:What is std::promise?

I'm fairly familiar with C++11's std::thread , std::async and std::future components (eg see this answer ), which are straight-forward. 我非常熟悉C ++ 11的std::threadstd::asyncstd::future组件(例如看到这个答案 ),这些都很简单。

However, I cannot quite grasp what std::promise is, what it does and in which situations it is best used. 但是,我无法完全理解std::promise是什么,它做什么以及在哪种情况下最好使用它。 The standard document itself doesn't contain a whole lot of information beyond its class synopsis, and neither does just::thread . 标准文档本身不包含其类概要之外的大量信息,也不仅仅是:: thread

Could someone please give a brief, succinct example of a situation where an std::promise is needed and where it is the most idiomatic solution? 有人可以给出一个简短,简洁的例子,说明需要std::promise的情况以及最常用的解决方案吗?


#1楼

参考:https://stackoom.com/question/kAiH/什么是std-promise


#2楼

In a rough approximation you can consider std::promise as the other end of a std::future (this is false , but for illustration you can think as if it was). 在粗略的近似中,您可以将std::promise视为std::future的另一端(这是假的 ,但为了说明,您可以认为它就好了)。 The consumer end of the communication channel would use a std::future to consume the datum from the shared state, while the producer thread would use a std::promise to write to the shared state. 通信通道的消费者端将使用std::future来从共享状态使用数据,而生产者线程将使用std::promise来写入共享状态。


#3楼

Bartosz Milewski provides a good writeup. Bartosz Milewski提供了很好的写作。

C++ splits the implementation of futures into a set of small blocks C ++将future的实现拆分为一组小块

std::promise is one of these parts. std :: promise是这些部分之一。

A promise is a vehicle for passing the return value (or an exception) from the thread executing a function to the thread that cashes in on the function future. promise是一种工具,用于将执行函数的线程的返回值(或异常)传递给函数future中的线程。

... ...

A future is the synchronization object constructed around the receiving end of the promise channel. 未来是围绕承诺通道的接收端构建的同步对象。

So, if you want to use a future, you end up with a promise that you use to get the result of the asynchronous processing. 因此,如果您想使用未来,最终会得到一个用于获取异步处理结果的承诺。

An example from the page is: 该页面的一个例子是:

promise<int> intPromise;
future<int> intFuture = intPromise.get_future();
std::thread t(asyncFun, std::move(intPromise));
// do some other stuff
int result = intFuture.get(); // may throw MyException

#4楼

std::promise is the channel or pathway for information to be returned from the async function. std::promise是从异步函数返回信息的通道或途径。 std::future is the synchronization mechanism thats makes the caller wait until the return value carried in the std::promise is ready(meaning its value is set inside the function). std::future是同步机制,它使调用者等待直到std::promise携带的返回值就绪(意味着它的值在函数内部设置)。


#5楼

In the words of [futures.state] a std::future is an asynchronous return object ("an object that reads results from a shared state") and a std::promise is an asynchronous provider ("an object that provides a result to a shared state") ie a promise is the thing that you set a result on, so that you can get it from the associated future. 用[futures.state]的话来说, std::future是一个异步返回对象 (“一个从共享状态读取结果的对象”),而std::promise是一个异步提供者 (“提供结果的对象”)到共享状态“)即,承诺是你设置结果的东西,这样你就可以从相关的未来获得它。

The asynchronous provider is what initially creates the shared state that a future refers to. 异步提供程序最初创建未来引用的共享状态。 std::promise is one type of asynchronous provider, std::packaged_task is another, and the internal detail of std::async is another. std::promise是一种异步提供者, std::packaged_task是另一种, std::async的内部细节是另一种。 Each of those can create a shared state and give you a std::future that shares that state, and can make the state ready. 其中每个都可以创建一个共享状态,并为您提供共享该状态的std::future ,并且可以使状态准备就绪。

std::async is a higher-level convenience utility that gives you an asynchronous result object and internally takes care of creating the asynchronous provider and making the shared state ready when the task completes. std::async是一个更高级别的便捷实用程序,它为您提供异步结果对象,并在内部负责创建异步提供程序并在任务完成时使共享状态准备就绪。 You could emulate it with a std::packaged_task (or std::bind and a std::promise ) and a std::thread but it's safer and easier to use std::async . 您可以使用std::packaged_task (或std::bindstd::promise )以及std::thread来模拟它,但它更安全,更容易使用std::async

std::promise is a bit lower-level, for when you want to pass an asynchronous result to the future, but the code that makes the result ready cannot be wrapped up in a single function suitable for passing to std::async . std::promise有点低级,因为当你想要将异步结果传递给将来时,但是使得结果准备好的代码不能被包装在适合传递给std::async的单个函数中。 For example, you might have an array of several promise s and associated future s and have a single thread which does several calculations and sets a result on each promise. 例如,您可能有一个包含多个promise和相关联的future的数组,并且有一个线程可以执行多次计算并在每个promise上设置结果。 async would only allow you to return a single result, to return several you would need to call async several times, which might waste resources. async只允许你返回一个结果,返回几次你需要多次调用async ,这可能会浪费资源。


#6楼

I understand the situation a bit better now (in no small amount due to the answers here!), so I thought I add a little write-up of my own. 我现在对情况有所了解了(由于这里的答案,我的情况不小!),所以我想我加了一点自己的写作。


There are two distinct, though related, concepts in C++11: Asynchronous computation (a function that is called somewhere else), and concurrent execution (a thread , something that does work concurrently). C ++ 11中有两个截然不同但相关的概念:异步计算(在其他地方调用的函数)和并发执行(一个线程 ,它可以同时工作)。 The two are somewhat orthogonal concepts. 这两者是有些正交的概念。 Asynchronous computation is just a different flavour of function call, while a thread is an execution context. 异步计算只是一种不同的函数调用,而一个线程是一个执行上下文。 Threads are useful in their own right, but for the purpose of this discussion, I will treat them as an implementation detail. 线程本身很有用,但为了讨论的目的,我将它们视为实现细节。


There is a hierarchy of abstraction for asynchronous computation. 异步计算有一个抽象层次结构。 For example's sake, suppose we have a function that takes some arguments: 例如,假设我们有一个带有一些参数的函数:

int foo(double, char, bool);

First off, we have the template std::future<T> , which represents a future value of type T . 首先,我们有模板std::future<T> ,它表示类型T的未来值。 The value can be retrieved via the member function get() , which effectively synchronizes the program by waiting for the result. 可以通过成员函数get()检索该值,该函数通过等待结果有效地同步程序。 Alternatively, a future supports wait_for() , which can be used to probe whether or not the result is already available. 或者,将来支持wait_for() ,可用于探测结果是否已经可用。 Futures should be thought of as the asynchronous drop-in replacement for ordinary return types. 应将期货视为普通退货类型的异步直接替代品。 For our example function, we expect a std::future<int> . 对于我们的示例函数,我们期望std::future<int>

Now, on to the hierarchy, from highest to lowest level: 现在,从层次结构开始,从最高级别到最低级别:

  1. std::async : The most convenient and straight-forward way to perform an asynchronous computation is via the async function template, which returns the matching future immediately: std::async :执行异步计算最方便,最直接的方法是通过async函数模板,它立即返回匹配的未来:

     auto fut = std::async(foo, 1.5, 'x', false); // is a std::future<int> 

    We have very little control over the details. 我们对细节几乎没有控制权。 In particular, we don't even know if the function is executed concurrently, serially upon get() , or by some other black magic. 特别是,我们甚至不知道函数是否同时执行,串行执行get()或其他一些黑魔法。 However, the result is easily obtained when needed: 但是,在需要时很容易获得结果:

     auto res = fut.get(); // is an int 
  2. We can now consider how to implement something like async , but in a fashion that we control. 我们现在可以考虑如何实现async这样的东西,但是我们控制的方式。 For example, we may insist that the function be executed in a separate thread. 例如,我们可能会坚持在单独的线程中执行该函数。 We already know that we can provide a separate thread by means of the std::thread class. 我们已经知道我们可以通过std::thread类提供一个单独的线程。

    The next lower level of abstraction does exactly that: std::packaged_task . 下一个较低的抽象级别就是这样: std::packaged_task This is a template that wraps a function and provides a future for the functions return value, but the object itself is callable, and calling it is at the user's discretion. 这是一个包装函数并为函数返回值提供未来的模板,但对象本身是可调用的,并且由用户自行决定调用它。 We can set it up like this: 我们可以像这样设置:

     std::packaged_task<int(double, char, bool)> tsk(foo); auto fut = tsk.get_future(); // is a std::future<int> 

    The future becomes ready once we call the task and the call completes. 一旦我们调用任务并且呼叫完成,未来就会变得准备就绪。 This is the ideal job for a separate thread. 这是单独线程的理想工作。 We just have to make sure to move the task into the thread: 我们必须确保任务移动到线程中:

     std::thread thr(std::move(tsk), 1.5, 'x', false); 

    The thread starts running immediately. 线程立即开始运行。 We can either detach it, or have join it at the end of the scope, or whenever (eg using Anthony Williams's scoped_thread wrapper, which really should be in the standard library). 我们可以detach它,或者在范围的最后join它,或者每当(例如使用Anthony Williams的scoped_thread包装器,它应该在标准库中)。 The details of using std::thread don't concern us here, though; 但是,使用std::thread的细节与我们无关; just be sure to join or detach thr eventually. 只是一定要最终加入或分离thr What matters is that whenever the function call finishes, our result is ready: 重要的是,只要函数调用完成,我们的结果就准备好了:

     auto res = fut.get(); // as before 
  3. Now we're down to the lowest level: How would we implement the packaged task? 现在我们降到最低级别:我们如何实现打包任务? This is where the std::promise comes in. The promise is the building block for communicating with a future. 这就是std::promise用武之地。承诺是与未来沟通的基石。 The principal steps are these: 主要步骤如下:

    • The calling thread makes a promise. 调用线程做出承诺。

    • The calling thread obtains a future from the promise. 调用线程从promise获得未来。

    • The promise, along with function arguments, are moved into a separate thread. 承诺以及函数参数被移动到一个单独的线程中。

    • The new thread executes the function and populates fulfills the promise. 新线程执行函数并填充履行承诺。

    • The original thread retrieves the result. 原始线程检索结果。

    As an example, here's our very own "packaged task": 举个例子,这是我们自己的“打包任务”:

     template <typename> class my_task; template <typename R, typename ...Args> class my_task<R(Args...)> { std::function<R(Args...)> fn; std::promise<R> pr; // the promise of the result public: template <typename ...Ts> explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { } template <typename ...Ts> void operator()(Ts &&... ts) { pr.set_value(fn(std::forward<Ts>(ts)...)); // fulfill the promise } std::future<R> get_future() { return pr.get_future(); } // disable copy, default move }; 

    Usage of this template is essentially the same as that of std::packaged_task . 该模板的用法与std::packaged_task用法基本相同。 Note that moving the entire task subsumes moving the promise. 请注意,移动整个任务包含移动承诺。 In more ad-hoc situations, one could also move a promise object explicitly into the new thread and make it a function argument of the thread function, but a task wrapper like the one above seems like a more flexible and less intrusive solution. 在更临时的情况下,还可以将promise对象显式地移动到新线程中并使其成为线程函数的函数参数,但是像上面那样的任务包装器似乎是一种更灵活,更少侵入性的解决方案。


Making exceptions 做例外

Promises are intimately related to exceptions. 承诺与例外密切相关。 The interface of a promise alone is not enough to convey its state completely, so exceptions are thrown whenever an operation on a promise does not make sense. 仅承诺的接口不足以完全传达其状态,因此每当对promise的操作没有意义时抛出异常。 All exceptions are of type std::future_error , which derives from std::logic_error . 所有异常都是std::future_error类型,它派生自std::logic_error First off, a description of some constraints: 首先,描述一些约束:

  • A default-constructed promise is inactive. 默认构造的promise是非活动的。 Inactive promises can die without consequence. 不活跃的承诺可能会死亡而没有后果。

  • A promise becomes active when a future is obtained via get_future() . 当通过get_future()获得未来时,承诺变为活动状态。 However, only one future may be obtained! 但是,只能获得一个未来!

  • A promise must either be satisfied via set_value() or have an exception set via set_exception() before its lifetime ends if its future is to be consumed. 如果要消耗其未来,则必须通过set_value()满足promise或通过set_exception()设置异常,然后才能终止它。 A satisfied promise can die without consequence, and get() becomes available on the future. 满意的承诺可以在没有后果的情况下死亡,并且get()在未来可用。 A promise with an exception will raise the stored exception upon call of get() on the future. 带有异常的promise将在将来调用get()时引发存储的异常。 If the promise dies with neither value nor exception, calling get() on the future will raise a "broken promise" exception. 如果承诺既没有值也没有异常,那么在将来调用get()会引发“破坏的承诺”异常。

Here is a little test series to demonstrate these various exceptional behaviours. 这是一个小测试系列,以展示这些各种特殊行为。 First, the harness: 首先,线束:

#include <iostream>
#include <future>
#include <exception>
#include <stdexcept>

int test();

int main()
{
    try
    {
        return test();
    }
    catch (std::future_error const & e)
    {
        std::cout << "Future error: " << e.what() << " / " << e.code() << std::endl;
    }
    catch (std::exception const & e)
    {
        std::cout << "Standard exception: " << e.what() << std::endl;
    }
    catch (...)
    {
        std::cout << "Unknown exception." << std::endl;
    }
}

Now on to the tests. 现在进行测试。

Case 1: Inactive promise 案例1:不活跃的承诺

int test()
{
    std::promise<int> pr;
    return 0;
}
// fine, no problems

Case 2: Active promise, unused 案例2:积极承诺,未使用

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();
    return 0;
}
// fine, no problems; fut.get() would block indefinitely

Case 3: Too many futures 案例3:太多的未来

int test()
{
    std::promise<int> pr;
    auto fut1 = pr.get_future();
    auto fut2 = pr.get_future();  //   Error: "Future already retrieved"
    return 0;
}

Case 4: Satisfied promise 案例4:满意的承诺

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
    }

    return fut.get();
}
// Fine, returns "10".

Case 5: Too much satisfaction 案例5:太满足了

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_value(10);
        pr2.set_value(10);  // Error: "Promise already satisfied"
    }

    return fut.get();
}

The same exception is thrown if there is more than one of either of set_value or set_exception . 如果一个以上的相同抛出异常set_valueset_exception

Case 6: Exception 案例6:例外

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
        pr2.set_exception(std::make_exception_ptr(std::runtime_error("Booboo")));
    }

    return fut.get();
}
// throws the runtime_error exception

Case 7: Broken promise 案例7:承诺破碎

int test()
{
    std::promise<int> pr;
    auto fut = pr.get_future();

    {
        std::promise<int> pr2(std::move(pr));
    }   // Error: "broken promise"

    return fut.get();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值