Czy zmienne thread_local w C ++ 11 są automatycznie statyczne?

85

Czy istnieje różnica między tymi dwoma segmentami kodu:

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

i

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

Backstory: pierwotnie miałem wektor STATIC V (do przechowywania niektórych wartości pośrednich, jest on czyszczony za każdym razem, gdy wchodzę do funkcji) i program jednowątkowy. Chcę przekształcić program w program wielowątkowy, więc muszę jakoś pozbyć się tego statycznego modyfikatora. Moim pomysłem jest przekształcenie każdego statycznego w thread_local i nie martwić się o nic innego? Czy takie podejście może przynieść odwrotny skutek?

Zuza
źródło
16
Posiadanie thread_localzmiennej lokalnej nie ma sensu zaczynać od… każdy wątek ma swój własny stos wywołań.
Konrad Rudolph
1
Kilka funkcji C zostało pierwotnie napisanych w celu zwrócenia adresu zmiennych statycznych lub globalnych. Później stwierdzono, że prowadzi to do niejasnych błędów, gdy jest używane w aplikacjach wielowątkowych (np. Errno, czas lokalny). Ponadto, czasami bardzo szkodliwe jest zabezpieczenie współdzielonych zmiennych za pomocą muteksu, gdy funkcja jest wywoływana z wielu wątków lub konieczność przekazywania obiektu kontekstu wątku między wieloma obiektami i metodami wywołań. Zmienne, które są lokalne dla rozwiązania wątku te i inne kwestie.
edwinc
3
@Konrad Rudolph deklaruje zmienne lokalne wyłącznie jako staticzamiast static thread_localnie inicjuje jednej instancji zmiennej dla każdego wątku.
davide
1
@davide Nie o to chodzi, ani mnie, ani OP. Nie mówimy o staticvs, static thread_localale raczej o autovs thread_local, używając znaczenia auto(tj. Automatycznego przechowywania) sprzed C ++ 11 .
Konrad Rudolph
1
Zobacz także Jak definiować lokalne zmienne statyczne lokalne wątku? . Krótka uwaga prawnika językowego ... Obsługa Microsoft i TLS zmieniła się w Vista; zobacz Thread Local Storage (TLS) . Zmiana wpływa na rzeczy takie jak Singleton i może mieć zastosowanie lub nie. Jeśli używasz modelu oprogramowania abondware, prawdopodobnie wszystko będzie w porządku. Jeśli z przyjemnością obsługujesz wiele kompilatorów i platform, być może będziesz musiał zwrócić na to uwagę.
jww

Odpowiedzi:

92

Zgodnie ze standardem C ++

Gdy thread_local jest stosowany do zmiennej o zasięgu blokowym, zakłada się , że statyczny specyfikator klasy pamięci nie jest widoczny, jeśli nie pojawia się jawnie

Oznacza to więc, że ta definicja

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

jest równa

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

Jednak zmienna statyczna nie jest tym samym, co zmienna thread_local.

1 Wszystkie zmienne zadeklarowane za pomocą słowa kluczowego thread_local mają czas trwania wątku. Przechowywanie tych podmiotów powinno trwać przez czas trwania wątku, w którym zostały utworzone. W wątku istnieje odrębny obiekt lub odwołanie, a użycie zadeklarowanej nazwy odnosi się do jednostki skojarzonej z bieżącym wątkiem

Aby rozróżnić te zmienne, standard wprowadza nowy termin „ czas trwania wątku” wraz ze statycznym czasem trwania.

Vlad z Moskwy
źródło
1
statici externdlatego nie implikuje klasy pamięci, a jedynie powiązanie ze zmiennymi thread_local nawet w wewnętrznych zasięgach.
Deduplicator
4
@Deduplicator Zmienne zakresu blokowego nie są powiązane. Więc twoje CV jest błędne. Jak napisałem w poście, mają one czas przechowywania wątków. W rzeczywistości jest taki sam jak statyczny czas przechowywania, ale jest stosowany do każdego wątku.
Vlad z Moskwy
1
Jeśli dodasz extern, robisz deklarację, a nie definicję. Więc?
Deduplicator
1
@Deduplicator Oznacza to, że definicje zmiennych o zasięgu blokowym nie są powiązane.
Vlad z Moskwy
1
Właśnie to wypróbowałem w VS 2013 i krzyczy „Zmienne TL nie mogą być dynamicznie inicjalizowane”. Jestem zdziwiony.
v.oddou,
19

Tak, „pamięć lokalna wątku” jest bardzo podobna do „globalnej” (lub „pamięci statycznej”), tyle że zamiast „czasu trwania całego programu” masz „czas trwania całego wątku”. Tak więc zmienna lokalna bloku lokalnego wątku jest inicjowana, gdy kontrola przechodzi przez swoją deklarację po raz pierwszy, ale oddzielnie w każdym wątku i jest niszczona po zakończeniu wątku.

Kerrek SB
źródło
6

Kiedy używane z thread_local, staticjest implikowane w zakresie blokowym (patrz odpowiedź @ Vlada), wymagane dla członka klasy; Myślę, że oznacza połączenie dla zakresu przestrzeni nazw.

Na 9,2 / 6:

W definicji klasy element członkowski nie powinien być deklarowany za pomocą Thread_local Storage-Specifier, chyba że zostanie również zadeklarowany jako statyczny

Aby odpowiedzieć na pierwotne pytanie:

Czy zmienne thread_local w C ++ 11 są automatycznie statyczne?

Nie ma wyboru, z wyjątkiem zmiennych o zakresie przestrzeni nazw.

Czy istnieje różnica między tymi dwoma segmentami kodu:

Nie.

Vehsakul
źródło
4

Lokalny magazyn wątków jest statyczny, ale zachowuje się zupełnie inaczej niż zwykły magazyn statyczny.

Podczas deklarowania zmiennej statycznej istnieje dokładnie jedno wystąpienie zmiennej. Kompilator / system wykonawczy gwarantuje, że zostanie on zainicjowany kiedyś, zanim faktycznie go użyjesz, bez określania dokładnego czasu (niektóre szczegóły tutaj pominięto).

C ++ 11 gwarantuje, że ta inicjalizacja będzie bezpieczna wątkowo, jednak przed C ++ 11 to bezpieczeństwo wątku nie było gwarantowane. Na przykład

static X * pointer = new X;

może przeciekać instancje X, jeśli więcej niż jeden wątek trafi w statyczny kod inicjujący w tym samym czasie.

Kiedy deklarujesz zmienną wątek lokalną, istnieje potencjalnie wiele wystąpień zmiennej. Możesz myśleć o nich jako o mapach indeksowanych przez identyfikator wątku. Oznacza to, że każdy wątek widzi własną kopię zmiennej.

Ponownie, jeśli zmienna zostanie zainicjowana, kompilator / system wykonawczy gwarantuje, że inicjalizacja nastąpi przed użyciem danych i że inicjalizacja nastąpi dla każdego wątku, który używa tej zmiennej. Kompilator gwarantuje również, że inicjacja będzie bezpieczna wątkowo.

Gwarancja bezpieczeństwa wątków oznacza, że ​​może istnieć sporo zakulisowego kodu, który sprawi, że zmienna będzie zachowywać się tak, jak się tego spodziewasz - zwłaszcza biorąc pod uwagę, że kompilator nie ma możliwości z wyprzedzeniem dowiedzieć się, ile dokładnie wątków będzie istnieje w twoim programie i ile z nich dotknie zmiennej lokalnej wątku.

Dale Wilson
źródło
@Etherealone: ​​Interesujące. Jakie konkretne informacje? Czy możesz podać referencje?
Dale Wilson
1
stackoverflow.com/a/8102145/1576556 . Artykuł w Wikipedii C ++ 11 o tym wspomina, jeśli dobrze pamiętam.
Etherealone
1
Jednak obiekty statyczne są najpierw inicjowane, a następnie kopiowane. Dlatego też jestem trochę niejasny, czy inicjalizacja bezpieczna dla wątków obejmuje pełne wyrażenie. Prawdopodobnie tak jest, ponieważ w przeciwnym razie nie zostałby uznany za bezpieczny dla wątków.
Etherealone