В чем проблема с std:: async? - программирование

В чем проблема с std:: async?

В начале этот клип из С++ и Beyond я слышал что-то о проблемах с std::async. У меня есть два вопроса:

  • Для младшего разработчика существует ли набор правил для того, что делать и чего следует избегать при использовании std::async?

  • Каковы проблемы, представленные в этом видео? Связаны ли они с этой статьей?

4b9b3361

Ответ 1

Есть несколько проблем:

  • std::async без политики запуска позволяет библиотеке времени выполнения выбирать, запускать ли новый поток или запускать задачу в потоке, который называется get() или wait() в будущем. Как говорит Херб, это тот случай, который вы, скорее всего, захотите использовать. Проблема в том, что это оставляет его открытым для QoI библиотеки времени выполнения, чтобы получить правильное количество потоков, и вы не знаете, будет ли задача иметь поток для себя, поэтому использование локальных переменных потока может быть проблематичным. Это то, о чем беспокоит Скотт, как я понимаю.

  • Использование политики std::launch::deferred фактически не запускает задачу, пока вы явно не назовете get() или wait(). Это почти никогда не то, что вы хотите, поэтому не делайте этого.

  • Использование политики std::launch::async запускает новый поток. Если вы не отслеживаете, сколько потоков у вас есть, это может привести к запуску слишком большого количества потоков.

  • Herb обеспокоен поведением деструктора std::future, который должен дождаться завершения задачи, хотя MSVC2012 имеет ошибку в том, что он не ждет.

Для младшего разработчика я бы предложил:

  • Используйте std::async с политикой запуска по умолчанию.
  • Убедитесь, что вы явно ожидаете все свои фьючерсы.
  • Не используйте потоковое локальное хранилище в задачах async.

Ответ 2

Я не мог больше не согласиться с советом по использованию политики по умолчанию.

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

Неявное предположение о поведении по умолчанию заключается в том, что некоторый сложный механизм пула потоков оптимизирует размещение задач (возможно, позволяя некоторым запускать последовательно на CPU вызывающего абонента), но это чистая фантазия, поскольку ничто не указывает, что должно выполняться во время выполнения С++ (что в любом случае выходит за рамки среды выполнения компилятора).

Это больше похоже на поведение undefined по дизайну для меня.

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

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

Итак, я бы посоветовал указать launch::async, когда вы используете async, (говоря компилятору что-то вроде "эй, мне нужна какая-то задача асинхронизации, но действительно асинхронная, нормально?" ) и не использовать async на самом деле, если вы просто хотите последовательно выполнять задачи.

Если у вас возникли проблемы с вашими задачами async, может быть удобно вернуться к политике deferred, чтобы легче отлаживать их, но об этом.