Я пытаюсь установить некоторые эвристики, чтобы помочь мне выбрать подходящий класс std::thread
.
Как я понимаю, с самого высокого уровня (простейшего в использовании, но наименее гибкого) до самого низкого уровня мы имеем:
- std:: async с /std:: future (std:: shared_future) (если вы хотите выполнить однократную асинхронную нисходящую цепочку продлений)
- std:: packaged_task (если вы хотите назначить производителя, но отложите вызов к потоку)
- std:: prom (???)
Я думаю, что у меня есть приличное понимание , когда использовать первые два, но я до сих пор неясно о 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:: prom для реализовать некоторый библиотечный механизм), поэтому я могу более легко объяснить, как выбрать правильный класс для начинающего пользователя std::thread
.
Другими словами, было бы неплохо иметь полезный пример того, что я могу сделать с std::promise
, который не может выполняться с другими механизмами.
ANSWER
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
как необходимо).
Я думаю ответ SethCarnegie:
Легкий способ подумать о том, что вы можете либо установить будущее возвращая стоимость или используя обещание. будущее не имеет установленного метода; эта функциональность обеспечивается обещанием.
помогает уточнить, когда использовать обещание. Но мы должны иметь в виду, что может понадобиться std::mutex
, поскольку обещание может быть доступно из разных потоков, в зависимости от использования.
Кроме того, Дэвид Родригес отвечает, также отлично:
Потребительский конец канала связи будет использовать std:: future для извлечения данных из общего состояния, тогда как поток производителя будет использовать std:: prom для записи в общее состояние.
Но как альтернатива, почему бы просто не просто использовать std::mutex
в контейнере stl для результатов, а один поток или поток для производителей, чтобы действовать на контейнер? Что использует std::promise
, вместо этого, купите меня, помимо некоторой дополнительной удобочитаемости, против stl-контейнера результатов?
В версии std::promise
управление выглядит лучше:
- wait() будет блокироваться в определенном будущем до тех пор, пока не будет получен результат
- Если есть только один поток производителя, мьютекс не нужен
Следующий google-тест проходит как helgrind, так и drd, подтверждая, что с одним продюсером и с использованием wait() мьютекс не нужен.
TEST
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() );
}