Чи автоматичні змінні C_11 thread_local автоматично статичні?


85

Чи існує різниця між цими двома сегментами коду:

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

і

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Передісторія: спочатку я мав СТАТИЧНИЙ вектор V (для утримання деяких проміжних значень він очищається кожного разу, коли я ввожу функцію) та однопотокову програму. Я хочу перетворити програму на багатопотокову, тому я якось мушу позбутися цього статичного модифікатора. Моя ідея полягає в тому, щоб перетворити кожну статику на thread_local і не турбуватися ні про що інше? Чи може такий підхід дати зворотний ефект?


16
Наявність thread_localлокальної змінної не має сенсу починати з ... кожен потік має власний стек викликів.
Конрад Рудольф

1
Кілька функцій C були спочатку написані для повернення адреси статичних або глобальних змінних. Пізніше було виявлено, що це призводить до незрозумілих помилок при використанні в багатопотокових програмах (наприклад, errno, localtime). Крім того, іноді дуже шкідливо захищати спільні змінні за допомогою мьютексу, коли функція викликається з декількох потоків або потрібно передавати об'єкт контексту потоку серед багатьох об'єктів і методів викликів. Змінні, які є локальними для вирішення потоку ці та інші питання.
edwinc

3
@Konrad Rudolph оголошує локальні змінні виключно як, staticа static thread_localне ініціалізує один екземпляр змінної для кожного потоку.
davide

1
@davide Справа не в цьому, ні в мене, ні в ОП. Ми говоримо не про staticvs, static thread_localа скоріше про autovs thread_local, використовуючи значення до C ++ 11 auto(тобто автоматичне зберігання).
Конрад Рудольф

1
Також див. Розділ Як визначити локальні локальні статичні змінні? . Короткий зауваження мовного юриста ... Підтримка Microsoft і TLS змінилася навколо Vista; див. Локальне сховище потоків (TLS) . Зміна стосується таких речей, як "Сінглтон", і може застосовуватися, а може і не застосовуватися. Якщо ви використовуєте програму абонентського програмного забезпечення, то, мабуть, у вас все буде добре. Якщо ви із задоволенням підтримуєте кілька компіляторів та платформ, можливо, вам доведеться звернути на це увагу.
jww

Відповіді:


92

Відповідно до стандарту C ++

Коли thread_local застосовуються до змінного блоку обсягу зберігання класу специфікатор статичний маються на увазі , якщо він не з'являється в явному вигляді

Тож це означає, що це визначення

void f() {
    thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

еквівалентно

void f() {
    static thread_local vector<int> V;
    V.clear();
    ... // use V as a temporary variable
}

Однак статична змінна - це не те саме, що змінна thread_local.

1 Усі змінні, оголошені за допомогою ключового слова thread_local, мають тривалість зберігання потоків. Зберігання для цих об'єктів триватиме протягом потоку, в якому вони створені. У кожному потоці є окремий об’єкт або посилання, і використання оголошеного імені стосується сутності, пов’язаної з поточним потоком

Для розрізнення цих змінних стандарт вводить новий термін тривалості зберігання потоків разом із статичною тривалістю зберігання.


1
staticі, externотже, не мають на увазі клас сховища, а лише зв'язок для змінних thread_local навіть у внутрішніх областях.
Дедулікатор

4
Змінні області блоку @Deduplicator не мають зв’язку. Отже, ваше резюме неправильне. Як я писав у дописі, у них тривалість зберігання потоків. Фактично це така ж статична тривалість зберігання, але застосовується до кожного потоку.
Влад з Москви

1
Якщо ви додаєте екстерн, ви робите декларацію, а не визначення. Тому?
Дедулікатор

1
@Deduplicator Отже, це означає, що визначення змінних області блоку не мають зв'язку.
Влад з Москви

1
Я просто спробував це у VS 2013, і це кричить "Змінні TL не можуть бути динамічно ініціалізовані". Я спантеличений.
v.oddou

19

Так, "локально-потокове сховище" дуже схоже на "глобальне" (або "статичне сховище"), тільки замість "тривалості всієї програми" ви маєте "тривалість всього потоку". Тож локально-локальна змінна-локальна змінна ініціалізується, коли елемент управління вперше проходить через її оголошення, але окремо всередині кожного потоку, і він знищується, коли потік закінчується.


6

При використанні з thread_local, staticмається на увазі в блок-області (див. Відповідь @ Vlad), необхідній для учасника класу; Думаю, означає зв’язок для простору імен.

За 9.2 / 6:

У межах визначення класу член не повинен бути оголошений за допомогою специфікатора-класу-потоку-потоку, якщо він також не оголошений статичним

Щоб відповісти на вихідне запитання:

Чи автоматичні змінні C_11 thread_local автоматично статичні?

Вибору немає, крім змінних простору імен.

Чи існує різниця між цими двома сегментами коду:

Немає.


4

Локальне сховище потоків є статичним, але воно поводиться зовсім інакше, ніж просто статичне сховище.

Коли ви оголошуєте змінну статичною, існує рівно один екземпляр змінної. Система компілятора / середовища виконання гарантує, що вона буде ініціалізована для вас колись до того, як ви її фактично використаєте, не вказуючи, коли саме (деякі деталі тут пропущено.)

C ++ 11 гарантує, що ця ініціалізація буде безпечною для потоків, однак до C ++ 11 ця безпека потоків не була гарантована. Наприклад

static X * pointer = new X;

може витікати примірники X, якщо більше ніж один потік потрапляє в статичний код ініціалізації одночасно.

Коли ви оголошуєте потік змінної локальним, потенційно може бути багато примірників змінної. Ви можете думати про них, як про те, що вони перебувають на карті, яка індексується за допомогою ідентифікатора потоку. Це означає, що кожен потік бачить свою копію змінної.

Знову ж таки, якщо змінна ініціалізована, система компілятора / середовища виконання гарантує, що ця ініціалізація відбудеться до використання даних і що ініціалізація відбудеться для кожного потоку, який використовує змінну. Компілятор також гарантує, що ініціація буде безпечною для потоків.

Гарантії безпеки потоків означають, що може бути досить багато закулісного коду, щоб змінна поводилась так, як ви очікуєте - особливо зважаючи на те, що компілятор не може заздалегідь точно знати, скільки потоків буде існують у вашій програмі, і скільки з них торкнеться локальної змінної потоку.


@Etherealone: ​​Цікаво. Яка конкретна інформація? Чи можете ви надати посилання?
Дейл Вілсон,

1
stackoverflow.com/a/8102145/1576556 . У статті Wikipedia C ++ 11 це згадується, якщо я добре пам’ятаю.
Etherealone

1
Однак статичні об'єкти спочатку ініціалізуються, а потім призначаються копії. Тож мені також трохи незрозуміло, чи включає повна безпека ініціалізація повний вираз. Це, мабуть, так і є, бо інакше це не вважатиметься безпечним для потоків.
Etherealone
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.