В то время как, например, функция histogram
async(..., histogram, ...)
возвращает ассоциативный массив, который обернут в объект типа future
. Последний является чем-то вроде histogram
, не вернет значение. Полученный ассоциативный массив помещается в объект типа future
, и мы наконец можем получить к нему доступ. Функция get
дает доступ к инкапсулированному результату.Рассмотрим еще один короткий пример. Взгляните на этот фрагмент:
auto x (f(1, 2, 3));
cout << x;
Вместо предыдущего кода мы могли написать следующее:
auto x (async(launch::async
cout << x.get()
Вот, по сути, и все. Выполнение задач в фоновом режиме никогда не было проще по стандартам С++. Осталось разрешить один момент: что означает launch::async
async(f, 1, 2, 3)
async
сама выберет, какую политику использовать. Это означает отсутствие уверенности в том, что другой поток вообще запустится или что выполнение будет просто отложено в другом потоке.Дополнительная информация
Следует рассмотреть последний момент. Предположим, мы пишем код следующим образом:
async(launch::async, f);
async(launch::async, g);
Это может привести к тому, что функции f
g
(в данном примере неважны возвращаемые ими значения) будут выполняться в конкурирующих потоках и в это же время будут запускаться разные задачи. При запуске такого кода мы увидим Почему же код блокируется? Разве async
future
был получен из вызова async
, имеющего политику launch::async
, то его деструктор выполнит Это значит, что
async
из данного короткого примера являются блокирующими, поскольку сроки жизни объектов типа future
, которые они возвращают, заканчиваются в одной строке! Можно исправить данную ситуацию, получив их возвращаемые значения и поместив в переменные с более длинным сроком жизни.Реализуем идиому «производитель/потребитель» с использованием std::condition_variable
В этом примере мы реализуем типичную программу, работающую по принципу «производитель/потребитель», в которой запускается несколько потоков. Основная идея заключается в том, что существует один поток, который создает элементы и помещает их в очередь. Еще один поток потребляет (использует) эти элементы. Если создавать нечего, то поток-производитель приостанавливается. При отсутствии в очереди элементов приостанавливается потребитель.
Оба потока имеют доступ к очереди и могут изменить, поэтому ее нужно защитить с помощью мьютекса.
Важно рассмотреть и следующий момент: что будет делать потребитель, если в очереди нет элементов? Будет ли он опрашивать очередь каждую секунду до тех пор, пока не увидит новые элементы? В этом нет необходимости, поскольку можно позволить потребителю подождать
Для таких событий в C++11 предоставляется удобная структура данных: std::condition_variable
Как это делается
Итак, в этом примере мы реализуем простую программу, работающую по принципу «производитель/потребитель», которая запускает одного производителя значений в отдельном потоке, а также одного потребителя в другом потоке.
1. Сначала выполним все необходимые директивы include
std
:#include
#include
#include
#include
#include
using namespace std;
using namespace chrono_literals;
2. Создадим экземпляр очереди простых численных значений и назовем ее q
condition_variable
и назовем его cv
. Переменная finished
представляет собой способ, с помощью которого производитель укажет потребителю, что других значений больше не будет:queue
mutex mut;
condition_variable cv;