Каким ограничением std:: async является Stroustrup?
В главе 5.3.5.3 учебника "Язык программирования С++" (4-е издание) Бьярне Страуструп пишет о функции std::async
.
Существует очевидное ограничение: даже не думайте использовать async()
для задач, которые обмениваются ресурсами, требующими блокировки - с помощью async()
вы даже не знаете, сколько thread
будет использоваться, потому что до async()
, чтобы решить, на основе того, что он знает о системных ресурсах, доступных во время вызова.
Аналогичное наставление можно найти в С++ 11-FAQ на своем веб-сайте.
"Простой" является наиболее важным аспектом дизайна async()
/future
; фьючерсы также могут использоваться с потоками в целом, но даже не думают использовать async()
для запуска задач, которые выполняют I/O, манипулируют мьютексами или другими способами взаимодействуют с другими задачами.
Интересно, что он не уточняет это ограничение, когда он более подробно возвращается к возможностям С++ 11 concurrency в § 42.4.6 своей книги. Еще более интересно, что в этой главе он фактически продолжает (по сравнению с выражением на своем сайте):
Простым и реалистичным использованием async()
было бы создание задачи для сбора ввода от пользователя.
Документация async
на cppreference.com
не содержит никаких таких ограничений вообще.
После прочтения некоторых предложений и обсуждений, которые приводят к стандарту С++ 11 в его окончательной форме (к сожалению, у меня нет доступа), я понимаю, что async
был включен очень поздно в С++ 11 стандарт, и было много дискуссий о том, насколько мощной должна быть эта функция. В частности, я нашел статью Async Tasks в С++ 11: не совсем там Bartosz Milewski - очень хорошее резюме проблем, которые необходимо учитывать при реализации async
.
Однако все обсуждаемые проблемы связаны с переменными thread_local
, которые не разрушаются, если поток перерабатывается, ложные взаимоблокировки или нарушения доступа к данным, если поток переключает свою задачу в середине действия, и обе задачи содержат mutex
или recursive_mutex
соответственно и так далее. Это серьезная проблема для разработчиков этой функции, но если я правильно понимаю, текущая спецификация async
требует, чтобы все эти детали были скрыты от пользователя, выполнив задачу либо в потоке вызывающего, либо как если бы новый поток был создан для задача.
Итак, мой вопрос: Что мне не разрешено делать с async
, что мне разрешено использовать с помощью thread
вручную и в чем причина этого ограничения?
Например, что-то не так в следующей программе?
#include <future>
#include <iostream>
#include <mutex>
#include <vector>
static int tally {};
static std::mutex tally_mutex {};
static void
do_work(const int amount)
{
for (int i = 0; i < amount; ++i)
{
// Might do something actually useful...
const std::unique_lock<std::mutex> lock {tally_mutex};
tally += 1;
}
}
int
main()
{
constexpr int concurrency {10};
constexpr int amount {1000000};
std::vector<std::future<void>> futures {};
for (int t = 0; t < concurrency; ++t)
futures.push_back(std::async(do_work, amount / concurrency));
for (auto& future : futures)
future.get();
std::cout << tally << std::endl;
}
Очевидно, что если среда выполнения решит запланировать все задачи в основном потоке, мы бесполезно будем приобретать мьютекс снова и снова без уважительной причины. Но, хотя это может быть неэффективным, это неверно.
Ответы
Ответ 1
"Проблема" с std:: async заключается в том, что по умолчанию вы не знаете, запускает ли он поток. Если ваша функция должна запускаться в отдельном потоке, это проблема, так как ваша функция может не работать до вызова функции get() или wait().
Вы можете передать std:: launch:: async, чтобы гарантировать, что функция запускается в собственном потоке, который похож на std:: thread, который не может отсоединиться BG.
Ответ 2
Вы указываете проблему.
Нитки могут быть переработаны...
Поэтому любое использование хранилища thread_local опасно.
Ваша петля справедлива - но, как вы сказали, может быть неэффективной.
Вы можете потребовать, чтобы язык создавал другой поток, используя std:: launch:: async.
Но потоки все равно могут быть переработаны.