С++ 11 поддержка ключевого слова thread_local в Visual Studio 11

Итак, существует список функций C++11, поддерживаемых визуальная студия.

Поддержка thread_local помечена как частичная. Мне не удалось найти объяснение, что именно здесь означает частичная поддержка. Они просто использовали псевдоним __declspec(thread)?

Я мог бы просто использовать boost::thread_specific_ptr, но, похоже, есть некоторые отчеты, которые boost::thread_specific_ptr работает медленно. Это может быть или не быть правдой.

В частности, мне нужен быстрый TLS на x86/x64 в самых последних версиях linux+gcc и windows+msvc. Быстро означает отсутствие системных вызовов, где это возможно (я думаю, что это возможно для вышеперечисленных платформ).


person Eloff    schedule 07.01.2012    source источник
comment
Visual C++ 11 Developer Preview не поддерживает ключевое слово thread_local.   -  person James McNellis    schedule 08.01.2012
comment
Насколько я понимаю, «частичный» здесь означает, что семантика поддерживается, но не через стандартный синтаксис/ключевое слово.   -  person ildjarn    schedule 08.01.2012
comment
@ybungalobill Либо GNU, POSIX, Microsoft, SUN, IBM и стандартный комитет C ++ предоставили средства для чего-то, что не требуется в хорошо разработанном программном обеспечении, либо ваше понимание ошибочно. Но я хотел бы услышать ваши аргументы.   -  person Eloff    schedule 08.01.2012
comment
@ybungalobill: Нет, TLS может быть полезен. Вы правы в том смысле, что TLS по сути является еще одним глобальным, но у глобальных есть свое применение.   -  person GManNickG    schedule 08.01.2012
comment
@GMan Eloff: когда используется TLS? Один случай — errno, GetLastError — это просто еще один способ возврата кодов ошибок. Это может быть удобнее, чем возвращать напрямую, но я категорически против этого вообще. Я не хочу, чтобы каждая библиотека добавляла одно слово хранилища в каждый мой поток, который может даже не использовать библиотеку. Другой случай: контексты, такие как контекст рендеринга OpenGL. Кто-то может возразить, что удобно установить контекст один раз, а затем предположить, что он глобальный, но затем попробовать выполнить рендеринг в несколько контекстов из одного потока или разработать оболочку ООП для контекста, чтобы понять, почему он неисправен.   -  person Yakov Galka    schedule 08.01.2012
comment
@ybungalobill: Ничто из этого не имеет отношения к TLS, это просто аргументы против глобальных переменных в целом. Это хорошо, но тут не в этом дело. Возьмем хорошее использование глобальных переменных, а затем заявим, что TLS никогда не улучшит его и поэтому не нужен в хорошей программе.   -  person GManNickG    schedule 08.01.2012
comment
@GMan: я никогда не видел хорошего использования глобальных переменных. Ну, кроме постоянных данных, но постоянные данные не обязательно должны быть TLS.   -  person Yakov Galka    schedule 08.01.2012
comment
@ybungalobill: std::cout? Менеджер памяти? ОПЕРАЦИОННЫЕ СИСТЕМЫ?   -  person GManNickG    schedule 08.01.2012
comment
@GMan: я не согласен с std::cout. Предполагая, что вы имеете в виду, что диспетчер памяти - это «куча», опять же: у вас может быть более одной кучи. Подумайте, почему STL любит распределители. «ОС» — это «постоянные данные» с точки зрения вашей программы, т. е. ОС не меняется во время работы вашей программы.   -  person Yakov Galka    schedule 08.01.2012
comment
@ybungalobill: я имел в виду реализацию ОС. А вот в случае с менеджерами памяти более одного не имеет никакого отношения к глобальному или нет. У вас может быть несколько глобальных куч. Как бы вы реализовали std::cout?   -  person GManNickG    schedule 08.01.2012
comment
@GMan: Хорошо, физически глобальные вещи могут быть глобальными (например, аппаратные регистры и физическая оперативная память), но они не имеют отношения к TLS. std::cout имеет состояние форматирования, поэтому это изменяемый объект. Я бы предпочел что-то вроде std::ostream local_cout(standard_output);, где standard_output не меняется во время выполнения процесса.   -  person Yakov Galka    schedule 08.01.2012
comment
У глобальных переменных @ybungalobill есть свое применение. Как и TLS. Например, что если вы хотите генерировать случайные числа. Использование локальных ГСЧ является дорогостоящим, а также потенциально плохой идеей в зависимости от начального числа. Имеет смысл использовать глобальный ГСЧ. Но это не потокобезопасно. Введите TLS, тогда у вас будет RNG для каждого потока. То же самое применимо, если вам нужен объединенный распределитель для определенного типа объекта, вы не сделаете его локальным, потому что вы явно хотите поделиться им. Вы можете сделать его потокобезопасным, создав пул для каждого потока (пока вы освобождаете объекты в том же потоке, в котором вы их выделяете)   -  person Eloff    schedule 08.01.2012
comment
@ybungalobill: Интересная идея, мне нравится. :) Тем не менее, есть еще глобальный аспект, а иногда глобальные вещи проще. Рассмотрим, например, файл журнала.   -  person GManNickG    schedule 08.01.2012
comment
Значение локального хранилища потока заключается в том, что вещи, которые в противном случае были бы глобальными для всей программы, можно сделать менее глобальными. Например, errno раньше был глобальным, и это означало, что многопоточные программы не могли надежно проверять наличие ошибок, сигнализируемых с помощью errno. TLS › глобальные. Глобальные переменные действительно должны быть локальными для потока, и вам нужно будет добавить специальное ключевое слово, чтобы получить глобальные переменные программы.   -  person bames53    schedule 08.01.2012
comment
@ybungalobill: TLS может быть важен в некоторых случаях. Я использую его в своей библиотеке, потому что мне нужно, чтобы некоторая информация сохранялась при вызовах библиотеки для каждого потока, и поскольку это библиотека, я не контролирую стек потока и не могу очень хорошо указать, что вызывающая сторона должны поместить какой-то конкретный объект в свой стек. Я не возражаю против того, что TLS обычно лучше избегать, но у него есть причина. Или, другими словами, хорошо спроектированные программы могут не нуждаться в TLS, но иногда нужны хорошо спроектированные библиотеки.   -  person jalf    schedule 08.01.2012
comment
@jalf: я не понимаю, почему вы не можете поручить вызывающей стороне создать контекст, который передается в вашу библиотеку. т.е. YourLibContext lib; lib.JumpFromTower();. Это дает вам еще большую гибкость и будет иметь на одно косвенное обращение меньше для каждого доступа по сравнению с TLS. Эллоф: что? Насколько местные ГСЧ дороже? ГСЧ — хороший пример, когда вам не не нужны ни глобальные, ни TLS.   -  person Yakov Galka    schedule 08.01.2012
comment
@ybungalobill: потому что это усложнит использование, сделает более подверженным ошибкам и затруднит модификацию существующего кода. Я экспериментировал с этим подходом, но использовать его стало бы больно :)   -  person jalf    schedule 08.01.2012
comment
@jalf: Это, безусловно, более многословно и, следовательно, может вызвать боль в простых случаях. Но это не более подвержено ошибкам. Я бы резюмировал это так: пусть вызывающая сторона решает, где выделить контекст, потому что он знает лучше, чем выделять контекст автоматически, не оставляя места для настройки и заставляя половину потоков платить за то, что они не используют.   -  person Yakov Galka    schedule 08.01.2012
comment
@ybungalobill: учитывая, что в моем случае контекст является абсолютно деталью реализации, и очень важно, чтобы у вас был ровно один контекст для каждого потока, обращающегося к моей библиотеке, я не согласен. Наличие двух контекстов в одном потоке нарушило бы семантику библиотеки. Я обдумал это довольно тщательно. ;)   -  person jalf    schedule 08.01.2012
comment
Библиотека предназначена для обработки синхронизации между потоками. Представьте, если бы вам приходилось везде передавать один и тот же контекст, чтобы использовать блокировки или мьютексы. Сделать этот контекст доступным с помощью обратных вызовов было бы крайне затруднительно. И если вы когда-нибудь создадите более одного контекста в одном потоке, это будет ошибкой, и синхронизация будет вести себя не так, как вы ожидаете. Я хотел бы избежать всех форм глобальных переменных, но в моем сценарии это действительно единственный подход, который работает.   -  person jalf    schedule 08.01.2012
comment
@ybungalobill нет, некоторые RNG имеют значительные накладные расходы, очевидно, что глупо выделять и заполнять это, если вы планируете генерировать только одно число внутри функции. Выделение ГСЧ выше стека вызовов и передача его через каждую функцию - ИМХО плохого дизайна. Но реальная проблема с локальными ГСЧ заключается в том, что вы должны тщательно выбирать начальное число. Создайте один внутри текущей функции, заполните их текущим временем (как обычно), затем сгенерируйте несколько чисел и вернитесь. Вызовите эту функцию внутри узкого цикла, и вы получите точно такое же начальное число (и числа) для большого количества вызовов!   -  person Eloff    schedule 11.01.2012
comment
@jalf: Хорошо, в вашем случае вам действительно нужен один экземпляр на поток по определению проблемы. Это та же причина, что и у вас, например. один стек на поток. Это проблемы, которые напрямую связаны с указателем инструкции. Я говорю о вещах, которым нет смысла привязывать свою жизнь к треду, но люди все еще думают, что это «хорошо». Например. генераторы случайных чисел. Нет Элофф. Ваш аргумент по-прежнему недействителен. Я не говорил, что у них нет накладных расходов. Я также не говорил создавать их каждый раз, когда вы хотите нарисовать число. Ни передать его как явный параметр.   -  person Yakov Galka    schedule 11.01.2012
comment
@Eloff: ... Вам просто нужно более тщательно проанализировать свой дизайн. Решите, какое время жизни ГСЧ вам нужно, и привяжите его к объекту с указанным временем жизни. Ты пишешь игру? Ничего страшного, ГСЧ будет членом состояния игры и будет везде передаваться косвенно через указатель на игру. Это симуляция Монте-Карло, работающая в нескольких потоках параллельно? Нет проблем, инициализируйте один ГСЧ в объекте, инкапсулирующем симуляцию.   -  person Yakov Galka    schedule 11.01.2012
comment
Преимущества: 1) не создаются избыточные копии для потоков, которые его не используют. 2) вы можете запускать два экземпляра симуляции в одном потоке (чередуя, если поддерживается) и результат не будет зависеть от порядка чередования. 3) вы можете сериализовать симуляцию и продолжить с того места, где вы остановились, даже на другой машине с другим количеством потоков. 4) вы можете выполнить модульное тестирование своего кода. Минусы: конечно, если ваш случай - простое домашнее задание распечатать список случайных слов, тогда этот дизайн не для вас, и даже старая добрая rand() под ваши нужды просто найдется.   -  person Yakov Galka    schedule 11.01.2012


Ответы (1)


Поэтому я немного покопался в семантике thread_local. __thread в gcc и __declspec(thread) в msvc имеют ту же семантику, что и thread_local (за исключением динамической инициализации, которая, возможно, еще не вошла в стандарт). Так что это действительно не проблема для моего варианта использования. Я просто сделаю определение, которое использует псевдонимы для того или иного атрибута конкретной платформы.

person Eloff    schedule 07.01.2012
comment
К сожалению, проблема с этими механизмами заключается в том, что они не поддерживают типы, отличные от POD. Когда поток завершается, я хочу, чтобы его объекты TLS вызывали свои деструкторы. Ни __thread, ни __declspec(thread) с этим не справятся. НО, если вам это не нужно, этот подход должен работать нормально - person jalf; 08.01.2012
comment
Нетривиальное строительство/разрушение вы все равно не получите бесплатно, так что если вам нужно то есть более производительные (нет это не слово, но так и должно быть) механизмы. Я выбрал контекст __thread*, а затем выделил контекст в стеке в методе запуска потока и установил контекст tls*, указывающий на него. Затем я получаю правильное строительство/разрушение, и доступ к нему должен быть почти настолько быстрым, насколько это возможно. - person Eloff; 11.01.2012