Когда стоит использовать 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
версия:
Следующий 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() );
}