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

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

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

  1. std :: async с / std :: future (std :: shared_future) (когда вы хотите выполнить одноразовый асинхронный поток-обработчик)
  2. std :: packaged_task (когда вы хотите назначить производителя, но отложить вызов на поток)
  3. 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 тот не могу быть сделано с другими механизмами.

ОТВЕТ

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

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

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

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

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

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

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

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

Но в качестве альтернативы, почему бы просто не использовать std::mutex на stl контейнере результатов, и один поток или пул потоков, чтобы действовать на контейнер? Что делает использование std::promiseвместо этого, купите меня, кроме некоторой дополнительной читабельности против контейнера результатов stl?

Контроль, кажется, лучше в std::promise версия:

  1. wait () будет блокировать заданное будущее, пока не будет получен результат
  2. если есть только один поток производителя, мьютекс не нужен

Следующий 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() );
}

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';
}

печать

Ожидание … Готово!

Результаты: 7 8 9

Фьючерсы используются со всеми тремя потоками, чтобы получить свои результаты, и promise используется с третьим, чтобы выполнить future другими способами, чем возвращаемое значение. Кроме того, один поток может выполнять несколько futureс различными значениями через promiseс, что он не может сделать иначе.

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

15

Другие решения

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

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();
}
2

По вопросам рекламы [email protected]