24

我正在尝试建立一些启发式方法来帮助我决定std::thread要使用的适当类。

据我了解,从最高级别(使用最简单,但最不灵活)到最低级别,我们有:

  1. std::async with/std::future (std::shared_future) (当你想在一次性的生产者线程异步上执行时)
  2. std::packaged_task (当您想分配生产者,但将调用推迟到线程时)
  3. std::promise (???)

我想我对何时使用前两个有一个不错的把握,但我仍然不清楚std::promise.

std::futurestd::async调用一起,有效地将产生回调/仿函数/lambda 转换为异步调用(根据定义,它立即返回)。单个消费者可以调用std::future::get()阻塞调用来获取结果。

std::shared_future只是一个允许多个消费者的版本。

如果您想将std::future值与生产者回调绑定,但希望将实际调用推迟到以后(当您将任务与生成线程相关联时)std::packaged_task这是正确的选择。但是现在,由于对应std::futurestd::package_task可以,在一般情况下,可以被多线程访问,我们可能不得不小心使用 a std::mutex。请注意std::async,在第一种情况下,我们不必担心锁定。

阅读了一些关于 promise 的有趣链接后,我想我了解它的机制以及如何设置它们,但我的问题是,你什么时候会选择使用 Promise 而不是其他三个?

我正在寻找更多应用程序级别的答案,例如经验法则(在上面的 3. 中填写 ???),而不是链接中的答案(例如,使用 std::promise 来实现一些库机制),因此我可以更轻松地向std::thread.

换句话说,如果有一个有用的例子来说明我可以用其他机制做不到的事情std::promise那就太好了。

回答

Astd::future是一个奇怪的野兽:一般来说,你不能直接修改它的值。

三个可以修改其值的生产者是:

  1. std::async通过异步回调,它将返回一个std::future实例。
  2. std::packaged_task,当传递给线程时,将调用其回调,从而更新std::future与该 关联的实例std::packaged_task。这种机制允许生产者的早期绑定,但稍后调用。
  3. 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

  1. wait() 将阻塞给定的未来,直到产生结果
  2. 如果只有一个生产者线程,则不需要互斥锁

以下 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() );
}
4

2 回答 2

22

您不选择使用 apromise 代替其他人,而是使用 apromise其他人一起完成a 。cppreference.com上的代码示例给出了使用所有四个的示例:future

#include <iostream>
#include <future>
#include <thread>
 
int main()
{
    // future from a packaged_task
    std::packaged_task<int()> task([](){ return 7; }); // wrap the function
    std::future<int> f1 = task.get_future();  // get a future
    std::thread(std::move(task)).detach(); // launch on a thread
 
    // future from an async()
    std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });
 
    // future from a promise
    std::promise<int> p;
    std::future<int> f3 = p.get_future();
    std::thread( [](std::promise<int>& p){ p.set_value(9); }, 
                 std::ref(p) ).detach();
 
    std::cout << "Waiting...";
    f1.wait();
    f2.wait();
    f3.wait();
    std::cout << "Done!\nResults are: "
              << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
}

印刷

等待...完成!

结果是:7 8 9

Futures 与所有三个线程一起使用以获取它们的结果,并且 apromise与第三个线程一起使用以future通过返回值以外的方式实现 a。此外,单个线程可以通过 s 实现多个future具有不同值promise的 s,否则它无法做到。

一个简单的想法是,您可以future通过返回值或使用 a 来设置 a promisefuture没有set方法;该功能由promise. 您根据情况允许选择所需的内容。

于 2013-01-11T17:48:59.123 回答
2

当您有两个级别的异步时,您需要使用 Promise。例如:

void fun()
{
std::promise<int> p;
std::future<int> f = p.get_future();
std::future<void> f2;
auto f3 = std::async([&]{
   // Do some other computation
   f2 = std::async([&]{ p.set_value(42);});
   // Do some other work
});
   // Do some other work
   // Now wait for the result of async work;
std::cout << f.get();
   // Do some other work again 
// Wait for async threads to complete
f3.wait();
f2.wait();
}
于 2013-01-11T17:57:11.090 回答