When to use promise over async or packaged_task?

后端 未结 2 1312
星月不相逢
星月不相逢 2020-12-22 22:34

When should I use std::promise over std::async or std::packaged_task? Can you give me practical examples of when to use each one of th

相关标签:
2条回答
  • 2020-12-22 22:53

    A promise is used to store a value that was calculated using e.g. a std::async. See http://en.cppreference.com/w/cpp/thread/promise

    I can imagine you wonder about the difference between std::packaged_task and std::async (in the most common approach, std::async starts a separate thread NOW to run function/lambda/etc with a (likely) expensive calculation. A std::packaged_task is used to wrap a function/lambda/etc with the current values of arguments so you can LATER run it, either synchronously or in a separate thread).

    Both std::packaged_task and std::async provide a std::future that will contain the RESULT of the wrapped function/lambda/etc once run. Internally, the std::future uses a std::promise to hold that result.

    0 讨论(0)
  • 2020-12-22 23:08

    std::async

    std::async is a neat and easy way to get a std::future, but:

    • It does not always it start a new thread; pass std::launch::async as a first parameter to force it.

      auto f = std::async( std::launch::async, func );
      
    • The std::~future destructor can block until the new thread finishes

      auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
      
      {
          auto f = std::async( std::launch::async, sleep, 5 );
      }
      

      Normally we expect that only .get() or .wait() blocks, but for a std::future returned from std::async, the destructor also may block, so be careful not to block your main thread just by forgetting about it.

    • If the std::future is stored in a temporary-life object, the std::async call will block immediately, so the following block will take 10 seconds if you remove the auto f = initializations:

      auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); };
      
      {
          auto f1 = std::async( std::launch::async, sleep, 5 );
          auto f2 = std::async( std::launch::async, sleep, 5 );
      }
      

    std::packaged_task

    std::packaged_task by itself has nothing to do with threads: it is just a functor and a related std::future. Consider the following:

    auto task = [](int i) { std::this_thread::sleep_for(std::chrono::seconds(5)); return i+100; };
    
    std::packaged_task< int(int) > package{ task };
    std::future<int> f = package.get_future();
    package(1);
    std::cout << f.get() << "\n";
    

    Here we just run the task by package(1), and after it returns, f is ready so no blocking on .get().

    There is a feature of std::packaged_task that makes it very useful for threads. Instead of just a function, you can initialize std::thread with a std::packaged_task which gives a really nice way of getting to the 'std::future'. Consider the following:

    std::packaged_task< int(int) > package{ task };
    std::future<int> f = package.get_future();
    std::thread t { std::move(package), 5 };
    
    std::cout << f.get() << "\n";       //block here until t finishes
    
    t.join();
    

    Because std::packaged_task is not copyable, you must move it to new thread with std::move.

    std::promise

    std::promise is a powerful mechanism. For example, you can pass a value to new thread without need of any additional synchronization.

    auto task = [](std::future<int> i) {
        std::cout << i.get() << std::flush;
    };
    
    std::promise<int> p;
    std::thread t{ task, p.get_future() };
    
    std::this_thread::sleep_for(std::chrono::seconds(5));
    p.set_value(5);
    
    t.join();
    

    New thread will wait for us on .get()


    So, in general, answering your question:

    • Use std::async only for simple things, e.g. to make some call non-blocking, but bear in mind the comments on blocking above.
    • Use std::packaged_task to easily get to a std::future, and run it as a separate thread

      std::thread{ std::move(package), param }.detach();
      

      or

      std::thread t { std::move(package), param };
      
    • Use std::promise when you need more control over the future.

    See also std::shared_future and on passing exceptions between threads std::promise::set_exception

    0 讨论(0)
提交回复
热议问题