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();
}

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

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


person Ongy    schedule 06.09.2014    source источник
comment
@ T.C. если это действительно так просто, и я не должен полагаться на их блокировку, вы должны добавить это в качестве ответа, чтобы я мог принять это   -  person Ongy    schedule 06.09.2014
comment
похоже на ошибку. Какой компилятор?   -  person ixSci    schedule 06.09.2014
comment
Хм, Скотт Мейерс приводит хороший аргумент эта спецификация std::async требует его блокировки. Интересно...   -  person T.C.    schedule 06.09.2014
comment
Вы можете отметить используемую реализацию, включая платформу. Поведение, которого вы, похоже, ожидаете, - это именно то, что я испытываю на моем clang3.4 impl (OS X)   -  person WhozCraig    schedule 06.09.2014
comment
пробовал в MSVC с MSVS 2013 на текущих патчах, я попробую clang / gcc и доложу   -  person Ongy    schedule 06.09.2014
comment
connect.microsoft.com/VisualStudio/feedback/details/810623   -  person T.C.    schedule 06.09.2014
comment
Хорошо, на linux с clang он работает, как ожидалось, он работает, как я ожидал, поэтому я думаю, что это важная часть здесь   -  person Ongy    schedule 06.09.2014
comment
T.C. Думаю, эта ссылка правильный ответ, спасибо   -  person Ongy    schedule 06.09.2014


Ответы (2)


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

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

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

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

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

person T.C.    schedule 06.09.2014
comment
+1 MS сделала что-то подобное с ошибкой регулярного выражения VS2010, касающейся неправильного добавления +1 к счетчику последовательностей. Результатом было использование их регулярного выражения с той конкретной функцией, которую вы должны были сломать в своем выражении, чтобы оно работало с их реализацией, но на самом деле было неправильным выражением и, следовательно, больше нигде не сработало. Как вы понимаете, решение MS не исправлять это в SP1, хотя они знали об этом задолго до этого, не было хорошо встречено (люди были в ярости). По крайней мере, здесь мы можем обернуть ваше будущее подвижным объектом, который вызывает .wait() при уничтожении, так что у вас есть жизнеспособная альтернатива. - person WhozCraig; 06.09.2014
comment
Спасибо за ссылку! Только что вылетел из-за этой ошибки. - person Mikhail; 25.11.2015

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

Освобождает любое общее состояние. Это означает

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

Обратите внимание на последний пункт. На мой взгляд, вы должны вызывать get в конце вашей области видимости.

person Franz    schedule 06.09.2014
comment
future's dtor должен заблокироваться, если это будущее было получено через async вызов. Не нужно get там - person ixSci; 06.09.2014
comment
@ixSci Это также так, если вы делаете это без std :: async? - person Franz; 06.09.2014
comment
Нет, std::async звонок особенный. Хотя это довольно сбивает с толку. Посмотрите ссылку @ T.C. разместил в комментариях к исходному вопросу. - person ixSci; 06.09.2014