Это слабо связано с вопросом: объединены ли std :: thread в C ++ 11? , Хотя вопрос отличается, намерение остается тем же:
Вопрос 1. Есть ли смысл использовать собственные пулы потоков (или сторонние библиотеки), чтобы избежать дорогостоящего создания потоков?
Вывод в другом вопросе заключался в том, что нельзя полагаться на то, что вас std::thread
объединят (это может быть, а может и нет). Однако, std::async(launch::async)
похоже, у него гораздо больше шансов на объединение.
Не думаю, что это вызвано стандартом, но, IMHO, я ожидал бы, что все хорошие реализации C ++ 11 будут использовать пул потоков, если создание потоков происходит медленно. Я ожидал, что только на платформах, где создание нового потока обходится недорого, они всегда порождают новый поток.
Вопрос 2: Я так думаю, но у меня нет фактов, подтверждающих это. Я вполне могу ошибаться. Это обоснованное предположение?
Наконец, здесь я привел пример кода, который сначала показывает, как, по моему мнению, можно выразить создание потока async(launch::async)
:
Пример 1:
thread t([]{ f(); });
// ...
t.join();
становится
auto future = async(launch::async, []{ f(); });
// ...
future.wait();
Пример 2: запустить и забыть поток
thread([]{ f(); }).detach();
становится
// a bit clumsy...
auto dummy = async(launch::async, []{ f(); });
// ... but I hope soon it can be simplified to
async(launch::async, []{ f(); });
Вопрос 3: Вы бы предпочли async
версии thread
версиям?
Остальное уже не часть вопроса, а только для уточнения:
Почему возвращаемое значение должно быть присвоено фиктивной переменной?
К сожалению, текущий стандарт C ++ 11 вынуждает вас фиксировать возвращаемое значение std::async
, иначе выполняется деструктор, который блокируется до завершения действия. Некоторые считают это ошибкой в стандарте (например, Херб Саттер).
Этот пример с cppreference.com прекрасно это иллюстрирует:
{
std::async(std::launch::async, []{ f(); });
std::async(std::launch::async, []{ g(); }); // does not run until f() completes
}
Еще одно уточнение:
Я знаю, что пулы потоков могут иметь и другое законное использование, но в этом вопросе меня интересует только аспект избежания дорогостоящих затрат на создание потоков .
Я думаю, что все еще бывают ситуации, когда пулы потоков очень полезны, особенно если вам нужен больший контроль над ресурсами. Например, сервер может решить обрабатывать только фиксированное количество запросов одновременно, чтобы гарантировать быстрое время отклика и повысить предсказуемость использования памяти. Пулы потоков здесь должны быть в порядке.
Локальные переменные потока также могут быть аргументом для ваших собственных пулов потоков, но я не уверен, актуально ли это на практике:
- Создание нового потока с
std::thread
запусками без инициализированных локальных переменных потока. Может быть, это не то, что вам нужно. - В потоках, созданных с помощью
async
, для меня это несколько непонятно, потому что поток можно было использовать повторно. Насколько я понимаю, сброс локальных переменных потока не гарантируется, но я могу ошибаться. - С другой стороны, использование собственных пулов потоков (фиксированного размера) дает вам полный контроль, если он вам действительно нужен.
std::async()
. Мне все еще любопытно посмотреть, как они поддерживают нетривиальные деструкторы thread_local в пуле потоков.
launch::async
то он обрабатывает ее так, как если бы она была единственной, launch::deferred
и никогда не выполняет ее асинхронно - по сути, эта версия libstdc ++ "выбирает" всегда использовать отложенный, если не указано иное.
std::async
могла бы быть прекрасной вещью для производительности - это могла бы быть стандартная система выполнения краткосрочных задач, естественно поддерживаемая пулом потоков. Прямо сейчас это просто какая- std::thread
то чушь, чтобы заставить функцию потока возвращать значение. Да, и они добавили избыточную «отложенную» функциональность, которая полностью перекрывает работу std::function
.
std::async(launch::async)
похоже, у них гораздо больше шансов быть объединенными». Нет, я считаю,std::async(launch::async | launch::deferred)
что это можно объединить. С помощью всего лишьlaunch::async
задача , как предполагается , будет запущен в новом потоке , независимо от того, что другие задачи выполняются. С политикойlaunch::async | launch::deferred
реализация может выбрать, какая политика, но, что более важно, откладывает выбор какой политики. То есть он может дождаться, пока поток в пуле потоков не станет доступным, а затем выбрать политику асинхронности.