Как функция может работать "как будто" в новом потоке, не делая этого?

Per [futures.async]/3 bullet 1 стандарта C++, когда функция f передается в std::async с помощью политики запуска std::launch::async, f будет запускаться "как если бы в новом потоке исполнения ".

Учитывая, что f может что-либо делать, включая цикл бесконечно и блокировать навсегда, как реализация может предложить поведение f работающего в своем потоке, фактически не запуская его в своем потоке? То есть, как реализовать в реализации возможность использовать "как будто" комнату для маневра, предоставляемую стандартом?

Ответ 1

Если посмотреть в C++ refs, как показано здесь и здесь, кажется, что цель "как будто" - позволить библиотеке реализовать некоторую степень свободы. Например, в нем говорится:

как будто порожден std :: thread (std :: forward (f), std :: forward (args)...), за исключением того, что если функция f возвращает значение или генерирует исключение, оно сохраняется в общем состоянии доступный через std :: future, который async возвращает вызывающему.

в первом источнике и

как будто объект потока строится с аргументами fn и args как аргументы, и к нему присоединяется доступ к общему состоянию возвращаемого будущего

во-вторых. Таким образом, похоже, что поведение похоже на поведение std::thread но может быть реализовано по-другому. Это интересно, поскольку фраза потока исполнения, которую вы приводите здесь, отличается от std::thread. Тем не менее, кажется, что оба источника понимают, как, если так.

Другим вариантом может быть, как предложил Франсуа Андри, чтобы разрешить использование threadpool, как выражено в первом источнике:

Функция async шаблона выполняет функцию f асинхронно (потенциально в отдельном потоке, который может быть частью пула потоков)

Ответ 2

Некоторые способы я могу думать о том, что f может запускаться "как будто" в новом потоке, не делая этого, если бы f фактически не использовало какое-либо состояние, совместно используемое с другими потоками; то реализация может запускать его как отдельный процесс (поскольку ему не требуется разделяемое пространство памяти), он также может просто запустить его в основном потоке как просто еще один вызов функции (если он может доказать, что f не блокирует или не имеет наблюдаемого побочные эффекты, которые будут отличаться при выполнении этого пути). Его также можно планировать для запуска в существующем (но неактивном) потоке (пуле потоков).

И если вы хотите быть глупым, я думаю, вы могли бы также рассмотреть идею не запускать f вообще, так как нет никаких гарантий относительно того, когда новые потоки будут запланированы для операционной системы, поэтому реализация зла может просто сказать, что ОС никогда не планирует ни одного потока, кроме основного потока, поэтому не работает f вообще, эквивалентно планированию его в новом потоке. Конечно, это глупо/глупо, и никакая разумная реализация никогда не будет делать этого, но теоретически язык допускает такую дегенеративную реализацию (я думаю).

Ответ 3

Преимущество этого решения заключается в том, что вы оптимизировали реализацию для конкретной роли в многопоточном мире. Поскольку я участвую в процедурах обновления программного обеспечения, я буду использовать такой пример. По соображениям оптимизации вы можете позвонить:

std::thread::hardware_concurrency();

Вы получите:

Возвращает количество параллельных потоков, поддерживаемых реализацией.

Предположим, что у вас есть результат равный 4. Вы хотите выполнить обновление многих вещей параллельно. Основной поток - это список отслеживания фьючерсов (3 слева) и время от времени проверяется, выполняется ли это или нет, и если это сделано, выполните следующее из списка ToDo. Прибыль здесь, например, если вы обновляете память другого типа, такую как 1-FileSystem, 2-EEPROM, 3-NOR-память или что-то в этом роде. Нет никакой прибыли для проверки цикла без задержки, поэтому вы хотите задать задачу для 4-го потока между проверками. Вы пишете функцию, которая копает биткойны в течение 50 миллисекунд :), и вы запускаете ее как отложенную между проверками.

И тогда, как вы упомянули, мы имеем:

Преимущество "как будто" комнаты для маневра Стандарт обеспечивает

Ответ 4

AFAIK C++ позволяет управлять некоторыми внутренними (или специализированными) потоками, отличными от стандартных потоков.

Моя догадка (я все еще изучаю расширенные функции C++) заключается в том, что с флагом std::launch::async std::async() запустит f в новом внутреннем потоке.

Вы можете запустить f в новом стандартном потоке, используя std::thread. В этом случае исключения будут обрабатываться в вызываемом потоке, и основной код должен будет получить возвращаемое значение f.

С внутренним потоком возвращаемое значение и конечное исключение сохраняются в std::future, возвращаются std::async() и распределяются между потоками.

Итак, "как будто" означает "как будто я запускаю f с помощью std::thread но мне не нужно". Но наверняка, f будет работать на новом потоке.

Чтобы ответить на ваш вопрос о реализации, как только среда выполнения уже имеет стандартные потоки, это будет просто попытка специализировать их для конкретных целей. См. Также политику выполнения для алгоритмов, которые поддерживают распараллеливание.

Ответ 5

Я вижу основную идею "f будет работать", как если бы в новом потоке выполнения "" это то, что f будет выполняться асинхронно. Должна ли она быть новой нитью или чем-то еще, являются детали реализации.

Ответ 6

Единственное решение без Thread, я могу подумать о вчерашней "Time-Slicing", которая является формой многозадачности. Функции должны быть возвращены. В таком случае каждая функция будет работать так, как если бы они были на разных потоках, хотя практически они находятся в одном потоке.

Ответ 7

В документах он заявил там,

Функция async шаблона выполняет функцию f асинхронно (потенциально в отдельном потоке, который может быть частью пула потоков) и возвращает std :: future, который в конечном итоге приведет к результату вызова этой функции.

http://en.cppreference.com/w/cpp/thread/async