什么是std :: promise?


384

我相当熟悉C ++ 11分的std::threadstd::asyncstd::future部件(例如见这个答案),这是直接的。

但是,我不太了解std::promise它的用途,作用以及在什么情况下最好使用它。标准文档本身不包含除类概述之外的大量信息,而且::: thread也不包含。

有人可以举一个简短,简洁的例子来说明这种情况的std::promise需要,它是最惯用的解决方案?


2
以下是其中的一些代码:en.cppreference.com/w/cpp/thread/future
chris

58
真正非常简短的版本是: s来自std::promise何处std::futurestd::future是什么使您可以检索已答应给您的值。当您调用get()future时,它会一直等待直到std::promise与之一起设置值的所有者(通过调用set_valuepromise)。如果在设置值之前销毁了承诺,然后调用get()了与该承诺关联的Future,std::broken_promise则将得到一个例外,因为已向您承诺了一个值,但您不可能获得一个值。
James McNellis 2012年

14
我建议,如果可以的话/想,看看C ++并发在行动安东尼·威廉姆斯
大卫·罗德里格斯- dribeas

32
@KerrekSB std::broken_promise是标准库中命名最好的标识符。而且没有std::atomic_future
Cubbi 2012年

3
拒绝投票,想解释您的反对意见吗?
Kerrek SB 2013年

Answers:


182

用[futures.state]的话,a std::future是一个异步返回对象(“一个从共享状态读取结果的对象”),a std::promise是一个异步提供程序(“将结果提供给共享状态的对象”),即a许诺是您设定结果的依据,这样您就可以从相关的未来中获得它。

异步提供程序是最初创建将来引用的共享状态的对象。std::promise是一种类型的异步提供程序,std::packaged_task是另一种类型,而其内部详细信息std::async是另一种类型。这些中的每一个都可以创建一个共享状态,并为您提供一个std::future共享该状态的状态,并使状态就绪。

std::async是一个高级便利工具,它为您提供异步结果对象,并在内部负责创建异步提供程序,并在任务完成时使共享状态就绪。您可以使用std::packaged_task(或std::bindand std::promise)和a 来模拟它,std::thread但是它更安全,更易于使用std::async

std::promise是较低的级别,因为当您希望将异步结果传递给将来时,但是使结果准备就绪的代码不能包装在适合传递给的单个函数中std::async。例如,您可能有一个由多个promises和关联的futures组成的数组,并且有一个执行多个计算并为每个promise设置结果的线程。async只会让您返回一个结果,要返回多个结果则需要async多次调用,这可能会浪费资源。


10
可能浪费资源吗?如果该代码无法并行化,则可能不正确。
小狗

“异步返回”和“从共享状态读取结果”大多是正交的,这使第一句话有些混乱。您是说国家共享就在于未来与承诺之间吗?如果是这样,请从一开始就明确地说出来。
einpoklum

@einpoklum为什么您在最后一个单词之前停止阅读“异步返回对象”?我引用了该标准的术语。A future异步返回对象的具体示例,该对象是通过共享状态读取异步返回的结果的对象。A promise异步提供程序的具体示例,该对象是一个将值写入共享状态的对象,该值可以异步读取。我的意思是我写的。
乔纳森·威基利

496

我现在对情况的了解要好一些(由于这里的答案不多!),所以我想我自己写点文字。


C ++ 11中有两个截然不同但相关的概念:异步计算(一个在其他地方调用的函数)和并发执行(一个线程,可以并发工作的东西)。这两个是有点正交的概念。异步计算只是函数调用的另一种形式,而线程是执行上下文。线程本身就很有用,但是出于讨论的目的,我将其视为实现细节。


异步计算有一个抽象层次结构。例如,假设我们有一个带有一些参数的函数:

int foo(double, char, bool);

首先,我们有模板std::future<T>,它表示type的将来值T。可以通过成员函数检索该值get(),该函数通过等待结果来有效地同步程序。或者,将来的支持wait_for()可以用来探测结果是否已经可用。期货应被视为普通收益类型的异步替代品。对于示例函数,我们期望一个std::future<int>

现在,从最高级别到最低级别:

  1. std::async:执行异步计算最便捷的方法是通过async函数模板,该模板立即返回匹配的future:

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

    我们对细节几乎没有控制权。特别是,我们甚至不知道该函数是同时执行,串行执行get()还是通过其他一些魔术来执行。但是,在需要时很容易获得结果:

    auto res = fut.get();  // is an int
  2. 现在,我们可以考虑如何实现类似async,但在时尚是我们控制。例如,我们可能坚持要求函数在单独的线程中执行。我们已经知道我们可以通过std::thread该类提供单独的线程。

    下一个较低的抽象层正是这样做的:std::packaged_task。这是一个包装函数并为函数返回值提供未来的模板,但是对象本身是可调用的,并且调用方法由用户决定。我们可以这样设置:

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

    一旦我们调用任务并完成了调用,未来就已经准备就绪。这是单独线程的理想工作。我们只需要确保任务移到线程中即可:

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

    该线程立即开始运行。我们既可以选择detach它,也可以将join它放在范围的末尾,或者随时使用(例如,使用Anthony Williams的scoped_thread包装器,它实际上应该在标准库中)。使用的细节在std::thread这里与我们无关。只要确保thr最终加入或分离即可。重要的是,只要函数调用完成,我们的结果就准备好了:

    auto res = fut.get();  // as before
  3. 现在我们进入最低级别:我们如何实现打包的任务?这就是std::promise进来的地方。诺言是与未来进行沟通的基础。主要步骤如下:

    • 调用线程做出承诺。

    • 调用线程从promise中获得未来。

    • promise和函数参数一起被移到单独的线程中。

    • 新线程执行功能并履行承诺。

    • 原始线程检索结果。

    例如,这是我们自己的“打包任务”:

    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
    };

    此模板的用法与的用法基本相同std::packaged_task。请注意,移动整个任务包含移动承诺。在更特殊的情况下,也可以将一个Promise对象显式移动到新线程中,并使其成为线程函数的函数参数,但是像上面的那样,任务包装器似乎是一种更灵活且侵入性较小的解决方案。


设置例外

承诺与例外紧密相关。仅凭诺言的接口不足以完全传达其状态,因此只要对诺言的操作没有意义,就会抛出异常。所有异常的类型均std::future_error来自于std::logic_error。首先,描述一些约束:

  • 默认构造的promise是无效的。无效的承诺会死而没有后果。

  • 当通过获得未来时,promise就会生效get_future()。但是,只能获得一个未来!

  • 如果要使用承诺,则必须在其生存期结束之前通过履行承诺或通过set_value()设置例外set_exception()。满意的诺言可以毫无后果地消亡,并get()在将来可用。带有例外的承诺将get()在将来调用时引发所存储的例外。如果诺言既没有价值也没有例外,那么呼吁get()未来将引发“违背诺言”例外。

这是一个小测试系列,以演示这些各种异常行为。一,线束:

#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;
    }
}

现在开始测试。

情况1:无效承诺

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

情况2:有效承诺,未使用

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

情况3:太多的期货

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

案例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".

案例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();
}

如果有一个以上的相同抛出异常或者set_valueset_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

案例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();
}

您说:“ ...通过等待结果来有效地同步程序。” 。“同步”在这里是什么意思?整个陈述是什么意思?我不明白这一点。该词典条目中“同步”的含义都不能帮助我理解该句子。仅仅“等待”是否意味着“同步”?每个等待都同步吗?我想我部分理解了您的意思,但我不确定您的实际意思。
Nawaz 2013年

9
好的答案,谢谢您的帮助。关于std :: async的部分,我记得我们可以确定它会产生另一个线程或与flag(std :: launch :: async,std :: launch :: deferred)同步工作。
StereoMatching

1
@FelixDombek:完美的转发等std::function有很多构造函数;没有理由不将这些暴露给消费者my_task
Kerrek SB 2014年

1
@DaveedV .:感谢您的反馈!是的,这就是测试用例7:如果您在没有设置值或异常的情况下破坏了Promise,则调用get()Future会引发异常。我将通过添加“在销毁之前”来澄清这一点;请让我知道是否足够清楚。
Kerrek SB 2015年

3
最后,got()futurepromise令人惊奇的解释来嘲笑线程支持库!
阳光明媚的月亮

33

Bartosz Milewski提供了不错的文章。

C ++将期货的实现分为一组小块

std :: promise是这些部分之一。

Promise是一种工具,用于将执行函数的线程的返回值(或异常)传递给在未来使用函数兑现的线程。

...

Future是围绕promise通道的接收端构造的同步对象。

因此,如果您想使用未来,您最终会得到一个用来获取异步处理结果的承诺。

页面上的示例是:

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
最终,在线程的构造函数中看到了Promise的消息。Bartosz的文章也许不是最好的文章,但它确实解释了元素如何联系在一起。谢谢。
Kerrek SB 2012年

28

粗略地看,您可以将其std::promise视为a的另一端std::future(这是false,但是为了说明起见,您可以认为它确实是)。通信通道的使用者端将使用a std::future来消耗共享状态中的数据,而生产者线程将使用a std::promise来写入共享状态。


12
@KerrekSB:std::async可以在概念上(标准未强制要求)理解为创建a的函数std::promise,将其推入线程池(可能是线程池,可能是新线程,...)并返回std::future与呼叫者相关联。在客户端,您将等待,std::future另一端的线程将计算结果并将其存储在中std::promise。注意:该标准要求共享状态std::futurestd::promise在此特定用例中不存在。
DavidRodríguez-dribeas 2012年

6
@KerrekSB:std::future将不会join在线程上调用,它具有一个指向共享状态的指针,该共享状态是实际的通信缓冲区。该共享状态有一个同步机制(可能std::function+ std::condition_variable,直到锁呼叫者std::promise满足了。线程的执行是正交的这一切,并在许多实现,你可能会发现,std::async不被新的线程,然后加入执行,但而是由一个线程池,其寿命延长,直到节目结束。
大卫·罗德里格斯- dribeas

1
@DavidRodríguez-dribeas:请从评论中编辑信息到您的答案中。
Marc Mutz-mmutz 2012年

2
@JonathanWakely:这并不意味着它有一个新的线程来执行,只知道它有异步执行的,如果它是在新创建的线程运行。其主要优点std::async是,运行时库可以针对要创建的线程数为您做出正确的决定,并且在大多数情况下,我希望使用线程池的运行时。当前,VS2012确实在后台使用了线程池,并且没有违反as-if规则。请注意,对于这种特定的as-if几乎没有需要保证。
DavidRodríguez-dribeas 2012年

1
线程局部变量需要重新初始化,但是按条件规则允许任何操作(这就是为什么我将“好像”用斜体表示:)
Jonathan Wakely 2012年

11

std::promise是从异步函数返回信息的通道或途径。std::future是一种同步机制,它使调用者等待,直到传入的返回值std::promise就绪为止(这意味着其值已在函数内部设置)。


8

异步处理中实际上有3个核心实体。C ++ 11当前专注于其中的两个。

异步运行一些逻辑所需的核心内容是:

  1. 将在“某处”运行的任务(逻辑封装为某些函子对象)。
  2. 实际处理节点 -线程,处理等时,他们被提供给它运行这样的仿函数。查看“命令”设计模式以了解基本工作线程池如何执行此操作的好方法。
  3. 结果句柄:有人需要的是结果,而需要一个对象,将得到它为他们。由于OOP和其他原因,任何等待或同步都应在此句柄的API中完成。

C ++ 11调用了我在(1)中所说的东西std::promise,以及在(3)中所说的东西std::futurestd::thread是(2)公开提供的唯一内容。这是不幸的,因为实际程序需要管理线程和内存资源,并且大多数程序都希望任务在线程池上运行,而不是为每个小任务创建和销毁线程(这几乎总是会自己造成不必要的性能损失,并且可以轻松地创建资源饥饿,甚至更糟)。

根据Herb Sutter和C ++ 11大脑信任组织的其他人的说法,有一些暂定的计划来添加一个(std::executor类似于Java),将作为线程池和(2)的逻辑相似设置的基础。也许我们会在C ++ 2014中看到它,但是我的赌注更像是C ++ 17(如果他们破坏了这些标准,上帝会帮助我们)。


7

A std::promise被创建为承诺/未来对的端点,而std::future(使用get_future()方法从std :: promise创建)是另一个端点。这是一种简单的单发方法,它为两个线程提供了一种同步方式,因为一个线程通过消息将数据提供给另一线程。

您可以将其视为一个线程创建一个提供数据的承诺,而另一个线程在将来收集该承诺。此机制只能使用一次。

许诺/未来机制只是一个方向,从使用set_value()a方法std::promise的线程到使用get()a std::future方法接收数据的线程是一个方向。如果get()将来的方法被多次调用,则会生成一个异常。

如果与线程std::promise没有使用set_value()履行其承诺,那么当第二个线程调用get()std::future收集承诺,第二个线程将进入等待状态,直到承诺通过与第一个线程完成std::promise时,它使用的set_value()方法发送数据。

借助技术规范N4663编程语言—协程的C ++扩展和Visual Studio 2017 C ++编译器的提议协程,co_await也可以使用std::futurestd::async编写协程功能。请参阅https://stackoverflow.com/a/50753040/1466970中的讨论和示例,该部分作为一个部分讨论了std::futurewith 的使用co_await

以下示例代码是一个简单的Visual Studio 2013 Windows控制台应用程序,显示了使用一些C ++ 11并发类/模板和其他功能。它说明了对承诺/未来工作良好的用法,将执行某些任务并停止运行的自治线程,以及在需要更多同步行为且由于需要多个通知的情况下,承诺/未来对不起作用的用法。

关于此示例的一个注意事项是在各处添加的延迟。添加这些延迟只是为了确保打印到控制台使用的各种消息std::cout将是清楚的,并且不会混入来自多个线程的文本。

的第一部分main()是创建三个附加线程,std::promisestd::future在线程之间使用和发送数据。有趣的一点是,主线程启动了一个线程T2,该线程将等待主线程中的数据,然后执行某些操作,然后将数据发送到第三个线程T3,后者将执行一些操作并将数据发送回该线程。主线程。

的第二部分main()创建两个线程和一组队列,以允许从主线程到创建的两个线程中的每个线程的多个消息。我们不能使用std::promisestd::future为此,因为promise / future二人组是一枪,不能重复使用。

该类的来源Sync_queue来自Stroustrup的C ++编程语言:第4版。

// cpp_threads.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <thread>  // std::thread is defined here
#include <future>  // std::future and std::promise defined here

#include <list>    // std::list which we use to build a message queue on.

static std::atomic<int> kount(1);       // this variable is used to provide an identifier for each thread started.

//------------------------------------------------
// create a simple queue to let us send notifications to some of our threads.
// a future and promise are one shot type of notifications.
// we use Sync_queue<> to have a queue between a producer thread and a consumer thread.
// this code taken from chapter 42 section 42.3.4
//   The C++ Programming Language, 4th Edition by Bjarne Stroustrup
//   copyright 2014 by Pearson Education, Inc.
template<typename Ttype>
class Sync_queue {
public:
    void  put(const Ttype &val);
    void  get(Ttype &val);

private:
    std::mutex mtx;                   // mutex used to synchronize queue access
    std::condition_variable cond;     // used for notifications when things are added to queue
    std::list <Ttype> q;              // list that is used as a message queue
};

template<typename Ttype>
void Sync_queue<Ttype>::put(const Ttype &val) {
    std::lock_guard <std::mutex> lck(mtx);
    q.push_back(val);
    cond.notify_one();
}

template<typename Ttype>
void Sync_queue<Ttype>::get(Ttype &val) {
    std::unique_lock<std::mutex> lck(mtx);
    cond.wait(lck, [this]{return  !q.empty(); });
    val = q.front();
    q.pop_front();
}
//------------------------------------------------


// thread function that starts up and gets its identifier and then
// waits for a promise to be filled by some other thread.
void func(std::promise<int> &jj) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();   // wait for the promise attached to the future
    std::cout << "  func " << myId << " future " << ll << std::endl;
}

// function takes a promise from one thread and creates a value to provide as a promise to another thread.
void func2(std::promise<int> &jj, std::promise<int>&pp) {
    int myId = std::atomic_fetch_add(&kount, 1);   // get my identifier
    std::future<int> intFuture(jj.get_future());
    auto ll = intFuture.get();     // wait for the promise attached to the future

    auto promiseValue = ll * 100;   // create the value to provide as promised to the next thread in the chain
    pp.set_value(promiseValue);
    std::cout << "  func2 " << myId << " promised " << promiseValue << " ll was " << ll << std::endl;
}

// thread function that starts up and waits for a series of notifications for work to do.
void func3(Sync_queue<int> &q, int iBegin, int iEnd, int *pInts) {
    int myId = std::atomic_fetch_add(&kount, 1);

    int ll;
    q.get(ll);    // wait on a notification and when we get it, processes it.
    while (ll > 0) {
        std::cout << "  func3 " << myId << " start loop base " << ll << " " << iBegin << " to " << iEnd << std::endl;
        for (int i = iBegin; i < iEnd; i++) {
            pInts[i] = ll + i;
        }
        q.get(ll);  // we finished this job so now wait for the next one.
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::chrono::milliseconds myDur(1000);

    // create our various promise and future objects which we are going to use to synchronise our threads
    // create our three threads which are going to do some simple things.
    std::cout << "MAIN #1 - create our threads." << std::endl;

    // thread T1 is going to wait on a promised int
    std::promise<int> intPromiseT1;
    std::thread t1(func, std::ref(intPromiseT1));

    // thread T2 is going to wait on a promised int and then provide a promised int to thread T3
    std::promise<int> intPromiseT2;
    std::promise<int> intPromiseT3;

    std::thread t2(func2, std::ref(intPromiseT2), std::ref(intPromiseT3));

    // thread T3 is going to wait on a promised int and then provide a promised int to thread Main
    std::promise<int> intPromiseMain;
    std::thread t3(func2, std::ref(intPromiseT3), std::ref(intPromiseMain));

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2 - provide the value for promise #1" << std::endl;
    intPromiseT1.set_value(22);

    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.2 - provide the value for promise #2" << std::endl;
    std::this_thread::sleep_for(myDur);
    intPromiseT2.set_value(1001);
    std::this_thread::sleep_for(myDur);
    std::cout << "MAIN #2.4 - set_value 1001 completed." << std::endl;

    std::future<int> intFutureMain(intPromiseMain.get_future());
    auto t3Promised = intFutureMain.get();
    std::cout << "MAIN #2.3 - intFutureMain.get() from T3. " << t3Promised << std::endl;

    t1.join();
    t2.join();
    t3.join();

    int iArray[100];

    Sync_queue<int> q1;    // notification queue for messages to thread t11
    Sync_queue<int> q2;    // notification queue for messages to thread t12

    std::thread t11(func3, std::ref(q1), 0, 5, iArray);     // start thread t11 with its queue and section of the array
    std::this_thread::sleep_for(myDur);
    std::thread t12(func3, std::ref(q2), 10, 15, iArray);   // start thread t12 with its queue and section of the array
    std::this_thread::sleep_for(myDur);

    // send a series of jobs to our threads by sending notification to each thread's queue.
    for (int i = 0; i < 5; i++) {
        std::cout << "MAIN #11 Loop to do array " << i << std::endl;
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q1.put(i + 100);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
        q2.put(i + 1000);
        std::this_thread::sleep_for(myDur);  // sleep a moment for I/O to complete
    }

    // close down the job threads so that we can quit.
    q1.put(-1);    // indicate we are done with agreed upon out of range data value
    q2.put(-1);    // indicate we are done with agreed upon out of range data value

    t11.join();
    t12.join();
    return 0;
}

这个简单的应用程序创建以下输出。

MAIN #1 - create our threads.
MAIN #2 - provide the value for promise #1
  func 1 future 22
MAIN #2.2 - provide the value for promise #2
  func2 2 promised 100100 ll was 1001
  func2 3 promised 10010000 ll was 100100
MAIN #2.4 - set_value 1001 completed.
MAIN #2.3 - intFutureMain.get() from T3. 10010000
MAIN #11 Loop to do array 0
  func3 4 start loop base 100 0 to 5
  func3 5 start loop base 1000 10 to 15
MAIN #11 Loop to do array 1
  func3 4 start loop base 101 0 to 5
  func3 5 start loop base 1001 10 to 15
MAIN #11 Loop to do array 2
  func3 4 start loop base 102 0 to 5
  func3 5 start loop base 1002 10 to 15
MAIN #11 Loop to do array 3
  func3 4 start loop base 103 0 to 5
  func3 5 start loop base 1003 10 to 15
MAIN #11 Loop to do array 4
  func3 4 start loop base 104 0 to 5
  func3 5 start loop base 1004 10 to 15

1

诺言是另一端。

想象一下,你需要检索的值future通过计算之中async。但是,你不希望它在同一个线程来计算,你甚至不产生一个线程,“现在” -也许你的软件被设计成从池中选择一个线程,所以你不知道将会最后执行che运算。

现在,您将如何传递这个(至今未知的)线程/类/实体?您不会通过future,因为这是结果。您想要传递连接到的东西,future并代表电线的另一端,因此您将在查询future时不知道谁将实际计算/编写东西。

这是promise。这是连接到您的手柄future。如果future是一个扬声器,并与get()你开始听,直到一些声音出来,该promise是一个麦克风 ; 不仅是任何麦克风,它是通过单根导线连接到您所持扬声器麦克风。您可能知道对方在哪儿,但您不必知道它-您只需要给它,然后等到对方说些什么即可。


0

http://www.cplusplus.com/reference/future/promise/

一句话解释:furture :: get()永远等待promse :: set_value()。

void print_int(std::future<int>& fut) {
    int x = fut.get(); // future would wait prom.set_value forever
    std::cout << "value: " << x << '\n';
}

int main()
{
    std::promise<int> prom;                      // create promise

    std::future<int> fut = prom.get_future();    // engagement with future

    std::thread th1(print_int, std::ref(fut));  // send future to new thread

    prom.set_value(10);                         // fulfill promise
                                                 // (synchronizes with getting the future)
    th1.join();
    return 0;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.