我正在尝试建立一些启发式方法来帮助我决定std::thread
要使用的适当类。
据我了解,从最高级别(使用最简单,但最不灵活)到最低级别,我们有:
- std::async with/std::future (std::shared_future) (当你想在一次性的生产者线程异步上执行时)
- std::packaged_task (当您想分配生产者,但将调用推迟到线程时)
- std::promise (???)
我想我对何时使用前两个有一个不错的把握,但我仍然不清楚std::promise
.
std::future
与std::async
调用一起,有效地将产生回调/仿函数/lambda 转换为异步调用(根据定义,它立即返回)。单个消费者可以调用std::future::get()
阻塞调用来获取结果。
std::shared_future
只是一个允许多个消费者的版本。
如果您想将std::future
值与生产者回调绑定,但希望将实际调用推迟到以后(当您将任务与生成线程相关联时),std::packaged_task
这是正确的选择。但是现在,由于对应std::future
的std::package_task
可以,在一般情况下,可以被多线程访问,我们可能不得不小心使用 a std::mutex
。请注意std::async
,在第一种情况下,我们不必担心锁定。
阅读了一些关于 promise 的有趣链接后,我想我了解它的机制以及如何设置它们,但我的问题是,你什么时候会选择使用 Promise 而不是其他三个?
我正在寻找更多应用程序级别的答案,例如经验法则(在上面的 3. 中填写 ???),而不是链接中的答案(例如,使用 std::promise 来实现一些库机制),因此我可以更轻松地向std::thread
.
换句话说,如果有一个有用的例子来说明我可以用其他机制做不到的事情,std::promise
那就太好了。
回答
Astd::future
是一个奇怪的野兽:一般来说,你不能直接修改它的值。
三个可以修改其值的生产者是:
std::async
通过异步回调,它将返回一个std::future
实例。std::packaged_task
,当传递给线程时,将调用其回调,从而更新std::future
与该 关联的实例std::packaged_task
。这种机制允许生产者的早期绑定,但稍后调用。std::promise
,它允许一个人std::future
通过它的set_value()
调用来修改它的关联。通过这种对变异 a 的直接控制std::future
,如果有多个生产者(根据需要使用),我们必须确保设计是线程安全的std::mutex
。
我认为塞思卡内基的回答是:
一种简单的思考方式是,您可以通过返回值或使用承诺来设置未来。future 没有设置方法;该功能由 promise 提供。
有助于阐明何时使用 Promise。但是我们必须记住,astd::mutex
可能是必要的,因为根据使用情况,promise 可能可以从不同的线程访问。
此外,大卫的罗德里格斯的回答也很出色:
通信通道的消费者端将使用 std::future 来使用来自共享状态的数据,而生产者线程将使用 std::promise 来写入共享状态。
但作为替代方案,为什么不简单地std::mutex
在 stl 结果容器上使用 a ,并使用一个线程或生产者线程池对容器进行操作?除了一些额外的可读性与 stl 结果容器之外,使用std::promise
,相反,给我买了什么?
版本中的控制似乎更好std::promise
:
- wait() 将阻塞给定的未来,直到产生结果
- 如果只有一个生产者线程,则不需要互斥锁
以下 google-test 通过了 helgrind 和 drd,确认使用单个生产者并使用 wait(),不需要互斥锁。
测试
static unsigned MapFunc( std::string const& str )
{
if ( str=="one" ) return 1u;
if ( str=="two" ) return 2u;
return 0u;
}
TEST( Test_future, Try_promise )
{
typedef std::map<std::string,std::promise<unsigned>> MAP;
MAP my_map;
std::future<unsigned> f1 = my_map["one"].get_future();
std::future<unsigned> f2 = my_map["two"].get_future();
std::thread{
[ ]( MAP& m )
{
m["one"].set_value( MapFunc( "one" ));
m["two"].set_value( MapFunc( "two" ));
},
std::ref( my_map )
}.detach();
f1.wait();
f2.wait();
EXPECT_EQ( 1u, f1.get() );
EXPECT_EQ( 2u, f2.get() );
}