Ответ 1
Шаблон функции std::async
(часть шаблона <future>
) используется для запуска (возможно) асинхронной задачи. Он возвращает объект std::future
, который в конечном итоге сохранит возвращаемое значение функции параметра std::async
.
Когда значение необходимо, мы вызываем get() на экземпляр std::future
; это блокирует поток до тех пор, пока будущее не будет готово, а затем вернет значение. std::launch::async
или std::launch::deferred
можно указать в качестве первого параметра std::async
, чтобы указать, как выполняется задание.
-
std::launch::async
указывает, что вызов функции должен выполняться по собственной (новой) теме. (Возьмите комментарий пользователя @T.C.). -
std::launch::deferred
указывает, что вызов функции должен быть отложен до тех пор, пока в будущем не будет вызван либоwait()
, либоget()
. Собственность на будущее может быть перенесена в другой поток, прежде чем это произойдет. -
std::launch::async | std::launch::deferred
указывает, что реализация может выбрать. Это опция по умолчанию (если вы не укажете ее самостоятельно). Он может решить запустить синхронно.
В этом случае всегда запускается новый поток?
От 1. можно сказать, что новый поток всегда запускается.
Являются ли мои предположения [на std:: launch:: отложенные] правильными?
От 2. можно сказать, что ваши предположения верны.
Что это значит? [в связи с запуском или отсутствием нового потока в зависимости от реализации]
От 3., поскольку std::launch::async | std::launch::deferred
является параметром по умолчанию, это означает, что реализация функции шаблона std::async
решит, будет ли он создавать новый поток или нет. Это связано с тем, что некоторые реализации могут проверять наличие избыточного планирования.
Внимание
Следующий раздел не связан с вашим вопросом, но я думаю, что важно иметь в виду.
В стандарте С++ говорится, что если a std::future
содержит последнюю ссылку на общее состояние, соответствующее вызову асинхронной функции, то std:: future destructor должен блокироваться до тех пор, пока поток для асинхронной работы не завершится. Таким образом, экземпляр std::future
, возвращаемый std::async
, блокирует его деструктор.
void operation()
{
auto func = [] { std::this_thread::sleep_for( std::chrono::seconds( 2 ) ); };
std::async( std::launch::async, func );
std::async( std::launch::async, func );
std::future<void> f{ std::async( std::launch::async, func ) };
}
Этот вводящий в заблуждение код может заставить вас думать, что вызовы std::async
являются асинхронными, они фактически синхронны. Экземпляры std::future
, возвращаемые std::async
, являются временными и блокируются, потому что их деструктор вызывается правильно, когда std::async
возвращается, поскольку они не назначены переменной.
Первый вызов std::async
будет блокироваться в течение 2 секунд, а затем еще 2 секунды блокировки от второго вызова до std::async
. Мы можем думать, что последний вызов std::async
не блокируется, так как мы сохраняем возвращаемый экземпляр std::future
в переменной, но поскольку это локальная переменная, которая уничтожается в конце области действия, она фактически блокирует дополнительные 2 секунды в конце области действия, когда локальная переменная f уничтожена.
Другими словами, вызов функции operation()
блокирует любой поток, который он вызывается синхронно в течение приблизительно 6 секунд. Такие требования могут отсутствовать в будущей версии стандарта С++.
Источники информации, которые я использовал для компиляции этих заметок:
С++ Concurrency в действии: Практическая многопоточность, Энтони Уильямс
Сообщение блога Скотта Мейерса: http://scottmeyers.blogspot.ca/2013/03/stdfutures-from-stdasync-arent-special.html