52

我什么时候应该使用std::promiseover std::asyncor std::packaged_task?你能给我具体的例子来说明何时使用它们中的每一个吗?

4

2 回答 2

66

标准::异步

std::async是获得 a 的一种简洁而简单的方法std::future,但是:

  • 它并不总是启动一个新线程;枚举值std::launch::async可以作为第一个参数传递std::async,以确保创建一个新线程来执行由 指定的任务func,从而确保func异步执行。

      auto f = std::async( std::launch::async, func );
    
  • 析构函数std::future 可以阻塞直到新线程完成

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

通常我们只期望.get()or.wait()阻塞,但是对于std::future返回的 from std::async,析构函数也可能阻塞,所以请注意不要因为忘记它而阻塞主线程。

  • 如果std::future存储在临时对象中,调用将在对象销毁时阻塞,因此如果删除初始化std::async,接下来的块将需要10 秒。auto f =否则它只会阻塞5 秒,因为这两个睡眠将是并发的,由于阻塞结束时两个对象的破坏而等待两者完成:

      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::thread您可以使用 a进行初始化,而不仅仅是一个函数,std::packaged_task它提供了一种非常好的方式来访问“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是一种强大的机制。例如,您可以将值传递给新线程,而无需任何额外的同步。

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


所以,一般来说,回答你的问题:

  • 仅用于std::async简单的事情,例如使某些呼叫非阻塞,但请记住上面关于阻塞的注释。

  • 用于std::packaged_task轻松获取 a std::future,并将其作为单独的线程运行

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

或者

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

另请参阅std::shared_future并在线程之间传递异常std::promise::set_exception

于 2014-06-11T13:47:45.487 回答
-1

Promise 用于存储使用例如 std::async 计算的值。请参阅http://en.cppreference.com/w/cpp/thread/promise

我可以想象你想知道 std::packaged_task 和 std::async 之间的区别(在最常见的方法中,std::async 现在启动一个单独的线程来运行 function/lambda/etc 并进行(可能)昂贵的计算。A std::packaged_task 用于使用参数的当前值包装函数/lambda/etc,以便您以后可以同步或在单独的线程中运行它)。

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

于 2013-10-05T21:30:02.860 回答