【问题标题】:What is std::promise?什么是 std::promise?
【发布时间】:2012-06-15 18:50:00
【问题描述】:

我非常熟悉 C++11 的 std::threadstd::asyncstd::future 组件(例如,请参阅 this answer),它们很简单。

但是,我无法完全理解 std::promise 是什么、它的作用以及在哪些情况下最好使用它。标准文档本身不包含超出其类概要的大量信息,std::thread 也不包含。

有人可以给出一个简短的例子来说明需要std::promise 的情况以及它是最惯用的解决方案吗?

【问题讨论】:

  • 这里有一些代码:en.cppreference.com/w/cpp/thread/future
  • 真正非常简短的版本是:std::promisestd::futures 的来源。 std::future 允许您检索已承诺 给您的值。当您在未来调用 get() 时,它会等到 std::promise 的所有者设置值(通过在承诺上调用 set_value)。如果 promise 在设置值之前被销毁,然后您在与该 promise 关联的 future 上调用 get(),您将收到 std::broken_promise 异常,因为您被承诺了一个值,但您不可能得到一个.
  • 我建议,如果你可以/想要,看看Anthony WilliamsC++ Concurrency in Action
  • @KerrekSB std::broken_promise 是标准库中命名最好的标识符。而且没有std::atomic_future
  • 投反对票的人,想解释一下你的反对意见吗?

标签: c++ multithreading c++11 promise standard-library


【解决方案1】:

我现在对情况有了更好的了解(由于这里的答案,我理解了很多!),所以我想我自己写一点。


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


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

int foo(double, char, bool);

首先,我们有模板std::future<T>,它代表T 类型的未来值。可以通过成员函数get() 检索该值,该函数通过等待结果来有效地同步程序。或者,future 支持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 对象显式移动到新线程中,并使其成为线程函数的函数参数,但像上面这样的任务包装器似乎是一种更灵活且侵入性更小的解决方案。


例外处理

Promise 与异常密切相关。仅 Promise 的接口不足以完整地传达其状态,因此只要对 Promise 的操作没有意义,就会引发异常。所有异常都是std::future_error 类型,它派生自std::logic_error。首先,描述一些约束:

  • 默认构造的 Promise 处于非活动状态。不活跃的承诺可能会毫无后果地消失。

  • 当通过get_future() 获得未来时,承诺将变为活动状态。然而,只能获得一个未来!

  • 一个promise 必须要么通过set_value() 满足,要么通过set_exception() 设置一个异常,如果它的future 要被消耗,它的生命周期结束之前。一个满意的承诺可以毫无后果地消失,get() 在未来变得可用。带有异常的承诺将在未来调用get() 时引发存储的异常。如果 Promise 既没有值也没有异常而终止,则在未来调用 get() 将引发“broken Promise”异常。

这里有一个小测试系列来演示这些不同的异常行为。一、线束:

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

【讨论】:

  • 你说的“...通过等待结果来有效地同步程序。”。这里的“同步”是什么意思?整个声明是什么意思?我无法理解这一点。 this dictionary entry 中的“同步”的任何含义都不能帮助我理解这句话。 “等待”是否意味着“同步”?每个等待是否同步?我想我部分理解你的意思,但我不确定你实际上是什么意思。
  • 很好的答案,感谢您的帮助。关于 std::async 的部分,我记得我们可以确定它会产生另一个线程或与 flag(std::launch::async, std::launch::deferred)
  • @FelixDombek:完美转发等。std::function 有很多构造函数;没有理由不将这些内容暴露给my_task 的消费者。
  • @DaveedV.:感谢您的反馈!是的,那是测试用例 7:如果你在没有设置值或异常的情况下销毁了 Promise,那么在未来调用 get() 会引发异常。我将通过添加“在它被销毁之前”来澄清这一点;请让我知道这是否足够清楚。
  • 最后got() 我的future 在您的惊人解释的promise 上探索线程支持库!
【解决方案2】:

用 [futures.state] 的话来说,std::future 是一个异步返回对象(“从共享状态读取结果的对象”),std::promise 是一个异步提供者(“向共享状态提供结果的对象”)即承诺是您设置结果的东西,以便您可以获取 它来自相关的未来。

异步提供者最初创建了未来引用的共享状态。 std::promise 是一种异步提供程序,std::packaged_task 是另一种,std::async 的内部细节是另一种。它们中的每一个都可以创建一个共享状态,并为您提供一个共享该状态的std::future,并可以使状态准备就绪。

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

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

【讨论】:

  • 会浪费资源吗?如果该代码无法并行化,则可能不正确。
  • “异步返回”和“从共享状态读取结果”大部分是正交的,这使得第一句话有点混乱。你的意思是说状态的共享是在未来和承诺之间?如果是这样,请从一开始就明确说明。
  • @einpoklum 为什么在最后一句话之前停止阅读“异步返回对象”?我引用了标准的术语。 future异步返回对象 的具体示例,该对象通过共享状态读取异步返回的结果。 promise异步提供者 的具体示例,它是一个将值写入共享状态的对象,可以异步读取。我的意思是我写的。
【解决方案3】:

Bartosz Milewski 提供了很好的文章。

C++ 将期货的实现拆分成一个集合 小块

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

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

【讨论】:

  • 在线程的构造函数中看到了promise,终于赚到了钱。 Bartosz 的文章可能不是最伟大的,但它确实解释了这些元素是如何联系在一起的。谢谢。
【解决方案4】:

粗略来说,您可以将std::promise 视为std::future 的另一端(这是错误,但为了便于说明,您可以将其视为是)。通信通道的消费者端将使用std::future 来消费来自共享状态的数据,而生产者线程将使用std::promise 来写入共享状态。

【讨论】:

  • @KerrekSB: std::async 在概念上(这不是标准规定的)可以理解为创建std::promise 的函数,将其推入线程池(排序,可能是线程池,可能是一个新线程,...)并将关联的std::future 返回给调用者。在客户端,您将在std::future 上等待,而另一端的线程将计算结果并将其存储在std::promise 中。注意:标准要求 共享状态std::future,但在此特定用例中不存在 std::promise
  • @KerrekSB: std::future 不会在线程上调用join,它有一个指向共享状态的指针,它是实际的通信缓冲区。 shared state 有一个同步机制(可能是std::function + std::condition_variable 来锁定调用者,直到std::promise 完成。线程的执行与这一切正交,并且在许多实现中您可能会发现std::async 不是由随后加入的新线程执行的,而是由生命周期延长到程序结束的线程池执行的。
  • @DavidRodríguez-dribeas:请将来自 cmets 的信息编辑到您的答案中。
  • @JonathanWakely: 这并不意味着它必须在一个新线程中执行,只是它必须异步执行 as-if 它是在一个新线程中运行的创建线程。 std::async 的主要优点是运行时库可以为您做出关于要创建的线程数量的正确决策,并且在大多数情况下,我希望运行时使用线程池。目前 VS2012 确实在底层使用了线程池,并且不违反 as-if 规则。请注意,对于这个特定的as-if,需要满足的保证非常少。
  • 线程局部变量需要重新初始化,但是 as-if 规则允许任何事情(这就是为什么我把“as if”放在斜体中:)
【解决方案5】:

std::promise 是从异步函数返回信息的通道或路径。 std::future 是一种同步机制,它使调用者等待直到 std::promise 中携带的返回值准备好(意味着它的值在函数内部设置)。

【讨论】:

    【解决方案6】:

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

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

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

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

    根据 Herb Sutter 和 C++11 智囊团的其他人的说法,暂定计划添加一个 std::executor,这就像在 Java 中一样,将成为线程池的基础,并且在逻辑上类似于 (2) 的设置.也许我们会在 C++2014 中看到它,但我的赌注更像是 C++17(如果他们搞砸了这些标准,上帝会帮助我们)。

    【讨论】:

      【解决方案7】:

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

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

      promise/future 机制只有一个方向,从使用std::promiseset_value() 方法的线程到使用std::futureget() 的线程接收数据。如果多次调用future 的get() 方法会产生异常。

      如果带有std::promise 的线程没有使用set_value() 来实现它的promise,那么当第二个线程调用std::futureget() 来收集promise 时,第二个线程将进入等待状态,直到当第一个线程使用set_value() 方法发送数据时,该承诺由std::promise 的第一个线程实现。

      借助Technical Specification N4663 Programming Languages — C++ Extensions for Coroutines 的建议协程和co_await 的Visual Studio 2017 C++ 编译器支持,还可以使用std::futurestd::async 编写协程功能。请参阅https://stackoverflow.com/a/50753040/1466970 中的讨论和示例,其中一节讨论了std::futureco_await 的使用。

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

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

      main() 的第一部分是创建三个额外的线程并使用std::promisestd::future 在线程之间发送数据。一个有趣的地方是主线程启动了一个线程,T2,它将等待来自主线程的数据,做一些事情,然后将数据发送到第三个线程,T3,然后它会做一些事情并将数据发送回主线程。

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

      Sync_queue 类的来源来自 Stroustrup 的 The C++ Programming Language: 4th Edition。

      // 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
      

      【讨论】:

        【解决方案8】:

        承诺是电线的另一端。

        假设您需要检索由async 计算的future 的值。但是,您不希望它在同一个线程中计算,甚至“现在”都不生成线程 - 也许您的软件被设计为从池中选择一个线程,所以您不知道 最后会执行che计算。

        现在,您将什么传递给这个(但未知的)线程/类/实体?你没有通过future,因为这是结果。您想将已连接的东西传递给future,并且代表电线的另一端,因此您只需查询future而不知道谁将实际计算/编写一些东西。

        这是promise。它是一个连接到您的future句柄。如果future扬声器,而get() 您开始聆听直到发出一些声音,promise麦克风;但不仅仅是任何麦克风,它是 麦克风,通过单根电线连接到您持有的扬声器。你可能知道谁在另一端,但你不需要知道——你只需给它并等到对方说些什么。

        【讨论】:

          【解决方案9】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-14
            • 2018-12-22
            • 2012-12-26
            • 2017-07-22
            • 2020-02-02
            • 2018-01-24
            • 2010-09-11
            • 1970-01-01
            相关资源
            最近更新 更多