Выбрать главу

В стандартной библиотеке С++ есть две разновидности будущих результатов, реализованные в форме двух шаблонов классов, которые объявлены в заголовке <future>: уникальные будущие результаты (std::future<>) и разделяемые будущие результаты (std::shared_future<>). Эти классы устроены по образцу std::unique_ptr и std::shared_ptr. На одно событие может ссылаться только один экземпляр std::future, но несколько экземпляров std::shared_future. В последнем случае все экземпляры оказываются готовы одновременно и могут обращаться к ассоциированным с событием данным. Именно из-за ассоциированных данных будущие результаты представлены шаблонами, а не обычными классами; точно так же шаблоны std::unique_ptr и std::shared_ptr параметризованы типом ассоциированных данных. Если ассоциированных данных нет, то следует использовать специализации шаблонов std::future<void> и std::shared_future<void>. Хотя будущие результаты используются как механизм межпоточной коммуникации, сами по себе они не обеспечивают синхронизацию доступа. Если несколько потоков обращаются к единственному объекту-будущему, то они должны защитить доступ с помощью мьютекса или какого-либо другого механизма синхронизации, как описано в главе 3. Однако, как будет показано в разделе 4.2.5, каждый из нескольких потоков может работать с собственной копией std::shared_future<> безо всякой синхронизации, даже если все они ссылаются на один и тот же асинхронно получаемый результат.

Самое простое одноразовое событие — это результат вычисления, выполненного в фоновом режиме. В главе 2 мы видели, что класс std::thread не предоставляет средств для возврата вычисленного значения, и я обещал вернуться к этому вопросу в главе 4. Исполняю обещание.

4.2.1. Возврат значения из фоновой задачи

Допустим, вы начали какое-то длительное вычисление, которое в конечном итоге должно дать полезный результат, но пока без него можно обойтись. Быть может, вы нашли способ получить ответ на «Главный возрос жизни, Вселенной и всего на свете» из книги Дугласа Адамса[7]. Для вычисления можно запустить новый поток, но придётся самостоятельно позаботиться о передаче в основную программу результата, потому что в классе std::thread такой механизм не предусмотрен. Тут-то и приходит на помощь шаблон функции std::async (также объявленный в заголовке <future>).

Функция std::async позволяет запустить асинхронную задачу, результат которой прямо сейчас не нужен. Но вместо объекта std::thread она возвращает объект std::future, который будет содержать возвращенное значение, когда оно станет доступно. Когда программе понадобится значение, она вызовет функцию-член get() объекта-будущего, и тогда поток будет приостановлен до готовности будущего результата, после чего вернет значение. В листинге ниже оказан простой пример.

Листинг 4.6. Использование std::future для получения результата асинхронной задачи

#include <future>

#include <iostream>

int find_the_answer_to_ltuae();

void do_other_stuff();

int main() {

 std::future<int> the_answer =

  std::async(find_the_answer_to_ltuae);

 do_other_stuff();

 std::cout << "Ответ равен " << the_answer.get() << std::endl;

}

Шаблон std::async позволяет передать функции дополнительные параметры, точно так же, как std::thread. Если первым аргументом является указатель на функцию-член, то второй аргумент должен содержать объект, от имени которого эта функция-член вызывается (сам объект, указатель на него или обертывающий его std::ref), а все последующие аргументы передаются без изменения функции-члену. В противном случае второй и последующие аргументы передаются функции или допускающему вызов объекту, заданному в первом аргументе. Как и в std::thread, если аргументы представляют собой r-значения, то создаются их копии посредством перемещения оригинала. Это позволяет использовать в качестве объекта-функции и аргументов типы, допускающие только перемещение. Пример см. в листинге ниже.

вернуться

7

В книге «Путеводитель для путешествующих автостопом по галактике» был построен компьютер Deep Thought, который должен был найти «ответ на главный вопрос жизни, Вселенной и всего на свете». Оказалось, что ответ на вопрос — 42.