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
в конце своей области.