在何时应该使用std::promise
,而不是std::async
或std::packaged_task
?您能给我一些实际的例子吗?
在何时应该使用std::promise
,而不是std::async
或std::packaged_task
?您能给我一些实际的例子吗?
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::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
是一个强大的机制。例如,您可以将值传递给新线程,而无需任何额外的同步。
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
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来保存该结果。
std::future
和std::promise
具有“共享状态”,因此结果不存储在promise对象中,而是存储在共享状态中。 - user2622016packaged_task
不会打包参数的值,它仅持有一个函数对象和一个“共享状态”。 - user2622016