【发布时间】:2012-12-26 08:41:33
【问题描述】:
我正在尝试建立一些启发式方法来帮助我决定要使用的合适的 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 值与生产者回调绑定,但希望将实际调用推迟到稍后时间(当您将任务关联到生成线程时),@987654331 @是正确的选择。但是现在,由于std::package_task 对应的std::future 在一般情况下可以被多线程访问,我们可能不得不小心使用std::mutex。请注意,使用std::async,在第一种情况下,我们不必担心锁定。
阅读some interesting links on promise,我想我了解它的机制以及如何设置它们,但我的问题是,你什么时候会选择使用 Promise 而不是其他三个?
我正在寻找更多应用程序级别的答案,例如经验法则(在上面的 3. 中填写 ???),而不是链接中的答案(例如使用 std: :promise to implement some library mechanism),所以我可以更容易地向std::thread的初学者解释如何选择合适的类。
换句话说,如果有一个有用的例子来说明我可以用 std::promise 做什么,而 不能 用其他机制来做,那就太好了。
回答
std::future 是一头奇怪的野兽:一般情况下,你不能直接修改它的值。
三个可以修改其值的生产者是:
-
std::async通过异步回调,这将返回一个std::future实例。 -
std::packaged_task,当传递给线程时,将调用其回调,从而更新与该std::packaged_task关联的std::future实例。这种机制允许早期绑定生产者,但稍后调用。 -
std::promise,它允许通过其set_value()调用修改其关联的std::future。通过这种对std::future变异的直接控制,如果有多个生产者,我们必须确保设计是线程安全的(根据需要使用std::mutex)。
一种简单的思考方式是,您可以通过以下方式设定未来 返回一个值或使用一个承诺。 future 没有设置方法; 该功能由 Promise 提供。
有助于阐明何时使用承诺。但我们必须记住,std::mutex 可能是必要的,因为根据使用情况,promise 可能可以从不同的线程访问。
另外,David's Rodriguez's answer 也很棒:
通信通道的消费者端将使用 std::future 消费来自共享状态的数据,而生产者线程 将使用 std::promise 写入共享状态。
但作为一种替代方案,为什么不简单地在 stl 结果容器上使用std::mutex,并使用一个线程或生产者线程池对容器进行操作?使用std::promise,除了一些额外的可读性与结果的stl容器之外,还给我买了什么?
std::promise 版本的控制似乎更好:
- wait() 将阻塞给定的未来,直到产生结果
- 如果只有一个生产者线程,则不需要互斥锁
以下 google 测试通过了 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() );
}
【问题讨论】:
-
std::promise和std::future和std::thread配合使用;promise由执行线程给出,future由启动线程获取。 -
@SethCarnegie:我会留意这里的答案:P
-
当然,
std::promise不需要互斥体来为std::future提供结果,这就是重点,这就是它存在的原因,以替换显式与互斥锁的手动同步。同样,不要使用互斥锁来防止对std::future的多次访问,而是使用std::shared_future。 -
@JonathanWakely +1 thx 提示
-
问题里怎么会有答案?