何时使用 Promise 而不是 Async 或 Packaged_task?

60

在何时应该使用std::promise,而不是std::asyncstd::packaged_task?您能给我一些实际的例子吗?

2个回答

77

std::async

std::async 是一种简单而方便的方法来获取一个std::future,但是需要注意:

  • It does not always start a new thread; the enum value std::launch::async can be passed as the first argument to std::async in order to ensure that a new thread is created to execute the task specified by func, thus ensuring that func executes asynchronously.

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

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

通常我们期望只有.get().wait()会阻塞,但对于从std::async返回的std::future,析构函数也可能会阻塞,因此请小心不要仅仅因为忘记了它而阻塞主线程。

  • If the std::future is stored in a temporary object, the std::async call will block at the point of the object's destruction, so the following block will take 10 seconds if you remove the auto f = initializations. It will only block for 5 seconds otherwise, because the two sleeps will be concurrent, with a wait for both to complete resulting from the destruction of the two objects at the end of the block:

      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本身与线程无关:它只是一个函数对象和相关的std::future。考虑以下内容:

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

在这里,我们只需通过package(1)运行任务,等它返回后,f就已经准备好了,所以.get()不会阻塞。

std::packaged_task的一个特性使其非常适用于线程。您可以使用std::packaged_task初始化std::thread,这是一种非常好的方法来获取“std::future”。请考虑以下内容:

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

因为std::packaged_task不可复制,所以必须使用std::move将其移动到新线程中。

std::promise

std::promise是一个强大的机制。例如,您可以将值传递给新线程,而无需任何额外的同步。

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

.get()方法会在新的线程等待我们。


因此,总的来说,回答你的问题:

  • 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();
    

或者

    std::thread t { std::move(package), param };
  • 当您需要更多对未来控制时,请使用std::promise

另请参见std::shared_future,以及有关在线程之间传递异常的信息,请参见std::promise::set_exception


-1

Promise(承诺)用于存储使用std::async等函数计算得到的值。请参阅 http://en.cppreference.com/w/cpp/thread/promise

我可以想象你会好奇std::packaged_task和std::async之间的区别(在最常见的方法中,std::async立即启动一个单独的线程来运行带有(可能)昂贵计算的函数/lambda等。 std::packaged_task用于包装带有参数当前值的函数/lambda等,以便稍后同步或在单独的线程中运行)。

std::packaged_task和std::async都提供了std::future,将包含运行包装的函数/lambda等的结果。在内部,std::future使用std::promise来保存该结果。


2
std::futurestd::promise具有“共享状态”,因此结果不存储在promise对象中,而是存储在共享状态中。 - user2622016
1
packaged_task 不会打包参数的值,它仅持有一个函数对象和一个“共享状态”。 - user2622016

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接