Подтвердить что ты не робот

Почему деструктор будущего возвращается из блокировки std:: async?

При попытке ответить на другой вопрос qaru.site/info/202394/... я понял, что этот простой фрагмент С++ 11 неявно блокирует вызывающий поток:

std::async(std::launch::async, run_async_task)

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

Итак, вот мой вопрос: есть ли какая-либо причина в отношении безопасности/правильности, которую должен блокировать деструктор std::future? Было бы недостаточно, если бы он блокировался только на get и в противном случае, если меня не интересует возвращаемое значение или исключение, оно просто срабатывает и забывается?

4b9b3361

Ответ 1

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

Несмотря на то, что было много обсуждений, для С++ 14 не запланировано никаких изменений в отношении поведения блокировки деструкторов std:: future и std:: thread.

Что касается вашего вопроса, самая интересная статья, вероятно, вторая - Хансом Бёмом. Я цитирую некоторые части, чтобы ответить на ваш вопрос.

N3679: ожидающие деструкторы Async() должны ждать

[..] Фьючерсы, возвращаемые async() с политикой запуска async, ждут в своем деструкторе, чтобы связанное с ним состояние стало готовым. Это предотвращает ситуацию, когда связанный поток продолжает работать, и больше не существует способа дождаться завершения, потому что связанное будущее было уничтожено. Без героических усилий, чтобы иначе ждать завершения, такой поток "бежать" может продолжать работать за время жизни объектов, от которых он зависит.

[Пример]

Конечный результат, скорее всего, будет "потоком памяти" с перекрестной резьбой. Разумеется, эту проблему избегают, если get() или wait() вызывается [..], прежде чем они [фьючерсы] будут уничтожены. Трудность [..] заключается в том, что неожиданное исключение может привести к обходу этого кода. Таким образом, для обеспечения безопасности обычно требуется какой-то контроль степени. Если программист забыл добавить защитную систему области видимости, представляется вероятным, что злоумышленник может генерировать, например. исключение bad_alloc в подходящей точке, чтобы воспользоваться надзором и вызвать перезапись стека. Возможно также управлять данными, используемыми для перезаписи стека, и таким образом получить контроль над процессом. Это достаточно тонкая ошибка, которая, по нашему опыту, скорее всего, будет упущена в реальном коде.

Обновление: Отчет о поездке Майкла Вона также содержит интересную информацию об итогах встречи в сентябре 2013 года:

Вид со стандартного собрания С++ Сентябрь 2013 г. Часть 2 из 2.

В связи с тем, что асинхронные деструкторы не должны блокировать, мы посвятили большое обсуждение этого вопроса. [..] Единственная позиция, которая получила значительную поддержку, была [..], давая советы о том, что будущие деструкторы не будут блокироваться, если они не возвращаются из async, что делает его заметным исключением. [...] После значительного обсуждения единственной частью, которую мы пытались перенести, была N3776, попытка прояснить позицию, которую ~future и ~shared_future не блокировать, за исключением, возможно, наличия асинхронизации. Была предпринята попытка выпустить устаревшую версию в строках C. Запретить асинхронную замену без замены. Это движение было фактически выдвинуто. Но [...] он умер еще до того, как он достиг операционного стола.