Promises and Multiple Waiting Threads

Single Producer with Multiple Consumers

  • Single producer thread

    • Produces a result or an event
  • Multiple consumer threads

    • Use the result
    • Or wait for the event to occur
  • Used in many applications

std::future and Multiple Waiting Threads

  • Designed for use with a single consumer thread

    • Assumes it has exclusive read access to the shared state
  • Cannot be safely shared between threads

    • Data race
  • Cannot be copied

    • Move only class

std::shared_future

  • Can be copied
    • Each thread has its own object
    • They all share the same state with the std::promise
    • Calling get() or wait() from different copies is safe

Obtaining an std::shared_future object

  • Normally, we do not create a shared future directly

  • We can move from an existing std::future

    • std::shared_future<int> shared_fut1 = std::move(fut);
  • We can call share() on the std::future

    • std::shared_future<int> shared_fut2 = fut.share();
  • We can also obtain a shared_future directly from a promise

    • std::shared_future<int> shared_fut3 = prom.get_future();
  • The producer will be the same as before

  • The consumer now takes an std::shared_future void consume(std::shared_future<int> &fx);


// Example using std::promise and std::future to send a result from a producer
// thread to a consumer thread
#include <functional>
#include <future>
#include <iostream>
#include <thread>

// The producer's task function takes a std::promise as argument
void produce(std::promise<int> &px) {
  using namespace std::literals;

  // Produce the result
  int x = 42;
  std::this_thread::sleep_for(2s);

  // Store the result in the shared state
  std::cout << "Promise sets shared state to " << x << '\n';
  px.set_value(x);
}

// The consumer's task function takes an std::future as argument
void consume(std::shared_future<int> &fx) {
  // Get the result from the shared state
  std::cout << "Future calling get()...\n";
  int x = fx.get();
  std::cout << "Future returns from calling get()\n";
  std::cout << "The answer is " << x << '\n';
}

int main() {
  std::promise<int> prom;
  std::shared_future<int> shared_fut1 = prom.get_future();
  std::shared_future<int> shared_fut2 = shared_fut1;

  std::thread thr_consumer(consume, std::ref(shared_fut1));
  std::thread thr_consumer2(consume, std::ref(shared_fut2));
  std::thread thr_producer(produce, std::ref(prom));

  thr_consumer.join();
  thr_consumer2.join();
  thr_producer.join();
}