标签: std::promise

  • std::future 与 std::promise 简单使用、简单原理

    C++11有了一些关于线程的模型,在此之前C++里可是各自为政的,各种线程库各种神奇用法。其中有两个好玩的东西就是std::promise<T>std::future<T>,下文书中形容它们像是个“虫洞”。

    • std::future是虫洞的出口:一个未来对象的“获取器”,在未来的某一刻它能返回一个有用的值,但现在还没有…
    • std::promise是虫洞的入口:我们要保证在未来的某一刻给一个值进去,但不是现在…

    Basic usage

    一般来说这俩是成对使用的,看个代码:

    #include <cassert>
    #include <chrono>
    #include <future>
    #include <iostream>
    #include <string>
    #include <thread>
    
    using namespace std::chrono_literals;
    
    int main()
    {
      std::promise<int> p1, p2;
      std::future<int> f1 = p1.get_future();
      std::future<int> f2 = p2.get_future();
      
      p1.set_value(42);
      assert(f1.get() == 42);
      
      std::thread t([&]() {
        std::this_thread::sleep_for(100ms);
        p2.set_value(43);
      });
      
      auto start_time = std::chrono::system_clock::now();
      assert(f2.get() == 43);
      std::chrono::duration<double, std::milli> elapsed = std::chrono::system_clock::now() - start_time;
      std::cout << "Waited " << elapsed.count() << " ms\n";
      t.join();
      return 0;
    }
    
    // output: Waited 100.253 ms                                                                                                                                                       
    

    一个future对象可以通过promise.get_future()方法创建出来。当我们有真正的值来填进promose对象的时候,就用promise.set_value(v)方法。同时,(一般是在另一个线程里)当准备要获取值的时候,就调用future.get()方法。get方法会保持阻塞状态直到一个有效值被填进去。

    值得一提的是,有一个特化的T = void。这货有啥用呢?可以用来阻塞等待某个并行任务完成:

    std::promise<void> ready_p;
    std::future<void> read_f = ready_p.get_future();
    
    std::thread thread_b([&]() {
        prep_work();
        ready_p.set_value(); // no arg
        main_work();
    });
    ready_f.wait();
    // now that thread B has completed
    

    A bit of details

    需要留意的是,promise也好future也罢,都是有动态内存分配(dynamic memory allocation)的开销的。
    std_promise_future_schema.PNG(图源为参考文献)

    留意图中的那个State对象,它基本上是一个shared_ptr——因为虫洞的两端(很可能是不同线程)都要用到这个共享对象(shared ownership)。所以创建std::promose/std::future的时候都是要申请新的堆空间。

    Reference

    本文全文参考自本书:
    <Mastering the C++17 STL: Make Full Use of the Standard Library Components in C++17>