Когда стоит использовать std :: обещание над другими механизмами std :: thread?

Я пытаюсь установить некоторую эвристику, чтобы помочь мне выбрать подходящийstd::thread класс для использования.

Насколько я понимаю, от самого высокого уровня (самого простого в использовании, но наименее гибкого) до самого низкого уровня мы имеем:

std :: async с / std :: future (std :: shared_future) (когда вы хотите выполнить одноразовую одноразовую асинхронность потока-производителя)std :: packaged_task (когда вы хотите назначить производителя, но отложить вызов на поток)std :: обещание (???)

Я думаю, что у меня есть приличное пониманиекогда использовать первые два, но до сих пор неясно,std::promise.

std::future в сочетании сstd::async вызов, эффективно преобразует производящий обратный вызов / функтор / лямбда в асинхронный вызов (который сразу возвращается по определению). Единственный потребитель может позвонитьstd::future::get(), блокирующий вызов, чтобы получить свои результаты обратно.

std::shared_future это просто версия, которая позволяет нескольким потребителям.

Если вы хотите связатьstd::future значение с обратным вызовом производителя,но хотите отложить фактический вызов на более позднее время (когда вы связываете задачу с порождающим потоком), std::packaged_task это правильный выбор. Но теперь, так как соответствующиеstd::future кstd::package_task в общем случае доступ к нему может быть получен из нескольких потоков, возможно, нам придется позаботиться об использованииstd::mutex, Обратите внимание, что сstd::asyncВ первом случае нам не нужно беспокоиться о блокировке.

Прочитавнекоторые интересные ссылки на обещаниеЯ думаю, я понимаю его механизмы и как их настроить, но мой вопрос,когда вы решите использовать обещание над остальными тремя?

Я больше ищу ответ на уровне приложения, например практическое правило (заполните ??? в п. 3. выше), а не ответ в ссылке (например, используйте std :: обещание для реализации некоторой библиотеки механизм), поэтому я могу более легко объяснить, как выбрать подходящий класс для начинающего пользователяstd::thread.

Другими словами, было бы неплохо иметь полезный пример того, что я могу сделать сstd::promise этоне можешь быть сделано с другими механизмами.

ОТВЕТ

A std::future странный зверь: в общем, вы не можете изменить его значение напрямую.

Три производителя, которые могут изменить его стоимость:

std::async через асинхронный обратный вызов, который вернетstd::future экземпляр.std::packaged_task, который при передаче потоку вызывает его обратный вызов, тем самым обновляяstd::future экземпляр, связанный с этимstd::packaged_task, Этот механизм позволяет раннее связывание производителя, но более поздний вызов.std::promise, что позволяет изменить его связанныйstd::future через егоset_value() вызов. С этим прямым контролем над мутациейstd::futureмы должны убедиться, что дизайн является поточно-ориентированным, если есть несколько производителей (используйтеstd::mutex по мере необходимости).

думаюСет Карнеги ответ:

Простой способ думать об этом состоит в том, что вы можете установить будущее, возвращая значение или используя обещание. будущее не имеет заданного метода; эта функциональность обеспечивается обещанием.

помогает уточнить, когда использовать обещание. Но мы должны помнить, чтоstd::mutex может потребоваться, так как обещание может быть доступно из разных потоков, в зависимости от использования.

Также,Ответ Родригеса Дэвида тоже отлично:

Конец потребителя канала связи будет использовать std :: future для получения данных из общего состояния, в то время как поток-производитель будет использовать std :: обещание для записи в общее состояние.

Но в качестве альтернативы, почему бы просто не использоватьstd::mutex на stl контейнере результатов, и один поток или пул потоков для действий на контейнер? Что делает использование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() );
}

Ответы на вопрос(2)

Ваш ответ на вопрос