什么时候使用std :: promise比其他std :: thread机制更好?

kfm*_*e04 18 c++ c++11 stdthread

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

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

  1. std :: async with/std :: future(std :: shared_future)(当你想在一次性丢弃的生产者线程async上执行时)
  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_taskcan,在一般情况下,可以由多线程访问,我们可能必须小心使用a std::mutex.请注意std::async,在第一种情况下,我们不必担心锁定.

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

我正在寻找一个应用程序级别的答案,比如一个经验法则(填写上面的???),而不是链接中的答案(例如使用std :: promise来实现一些库机制),所以我可以更容易地解释如何为初级用户选择合适的类std::thread.

换句话说,这将是很好有什么我可以用做一个有用的例子std::promise不能与其他机制来完成.

回答

A std::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必要时使用),我们必须确保设计是线程安全的.

我认为SethCarnegie的答案是:

想一想的一个简单方法是,您可以通过返回值或使用promise来设置未来.未来没有固定的方法; 该功能由promise提供.

有助于澄清何时使用承诺.但我们必须记住,std::mutex可能有必要,因为承诺可能可以从不同的线程访问,具体取决于使用情况.

此外,大卫的罗德里格兹的答案也非常好:

通信通道的消费者端将使用std :: future来从共享状态使用数据,而生产者线程将使用std :: promise来写入共享状态.

但作为替代方案,为什么不简单地只使用一个std::mutexstl容器的结果,一个线程或生产者的线程池作用于容器?是什么用std::promise,而是给我买,除了一些额外的可读性VS结果的STL容器?

控件似乎在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() );
}
Run Code Online (Sandbox Code Playgroud)

Set*_*gie 17

你不选择使用promise ,而不是别人的,你用一个promise履行一个future 结合与其他人.cppreference.com上的代码示例给出了使用全部四个的示例:

#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';
}
Run Code Online (Sandbox Code Playgroud)

版画

等待...完成!

结果是:7 8 9

期货与所有三个线程一起使用以获得其结果,并且a promise与第三个线程一起使用以future通过除返回值之外的方式来实现.此外,单个线程可以future通过promises 实现具有不同值的多个s,否则不能这样做.

想到它的一种简单方法是,您可以future通过返回值或使用a来设置promise.futureset办法; 该功能由promise.提供.根据情况允许,您可以根据需要选择.

  • 它有点旧线程,但我认为当`promise`比`packaged_task`好得多时,有一个很好的例子.假设你的工作线程做了一些任务并且有东西可以返回,但之后,有很多CLEAN UP任务.我认为在这种情况下最好设置一个承诺,这可能会解锁主线程,即等待这个未来,同时,用工作线程进行清理. (2认同)