Std:: list <std:: future> деструктор не блокирует

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

Для этой цели я создал std:: list, в который я помещал объекты std:: future, созданные для создания потоков

std::list<std::future<int>> threads;
threads.emplace_front(std::async(std::launch::async, ...));

У меня создалось впечатление, что если пробел list заканчивается, должен блокироваться, пока все потоки не возвратят свою главную функцию, потому что деструктор list будет разрушен все элементы std:: future и деструктор этих будет ждать завершения потока.

РЕДАКТИРОВАТЬ: Поскольку это актуально, я добавлю его здесь: Это на Win7 с версией MSVC в Visual Studio 2013 Professional /EDIT

Когда я это пробовал, он не блокировался, мне пришлось добавить

for (auto it = threads.begin(); it != threads.end(); ++it) {
    it->get();
}

до конца функции, чтобы правильно блокировать.

Я что-то пропустил, понимая что-то, или мне нужно создать поток по-другому, чтобы делать то, что я хочу здесь сделать?

Ответ 1

Это ошибка MSVC, исправленная, но исправление не будет доступно, пока MS не выпустит новую версию Visual С++, возможно, некоторое время в 2015 году. (Он также доступен в CTP для новой версии, но это довольно плохая идея использовать это для любого производственного кода...)

Как объяснил Скотт Майерс в своем сообщении в блоге, деструктор std::future, возвращаемый вызовом std::async с использованием политики launch::async, требуется для блокировки пока порожденная нить не завершит выполнение (§30.6.8 [futures.async]/p5):

Если реализация выбирает политику launch::async,

  • [...]
  • связанное завершение потока синхронизируется с (1.10) возврат из первой функции, которая успешно обнаруживает готовность статус общего состояния или возврат из последней функции который освобождает общее состояние, в зависимости от того, что произойдет раньше.

В этом случае деструктор future - это "последняя функция, которая освобождает общее состояние", поэтому завершение потока должно синхронизироваться с (то есть, раньше) возвратом этой функции.

Ответ 2

Я просмотрел документацию std:: future и нашел это для деструктора std:: future:

Релиз любого общего состояния. Это означает

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

Обратите внимание на последнюю точку. По-моему, вы должны вызвать get в конце своей области.