2016-09-18 6 views
1

Давайте начнем с кусочком code (Coliru):Thread локальная переменная доступна через не-нить локального объекта

#include <iostream> 
#include <thread> 

using namespace std; 

struct A 
{ 
    thread_local static A* p_a; 
    thread_local static int i; 
}; 

thread_local int A::i; 
thread_local A* A::p_a; 

int main() 
{ 
    A::p_a = new A; 

    auto lambda = [](A* a) 
     { 
      a->i = 1; // Prints 1 (below, of course) 
      std::cout << a->i << std::endl; 
     }; 

    std::thread t(std::bind(lambda, A::p_a)); 
    t.join(); 

    // Prints 0 (it hasn't been modified) 
    std::cout << A::p_a->i << std::endl; 

    return 0; 
} 

Как все вы можете видеть, второй поток изменяет свою нить локальную копию A::i, даже если я доступен это из другого потока локального объекта другого потока. Это ожидаемое поведение? Потому что невозможно получить информацию из другого потока с помощью «referer», если я не передаю указатель или ссылку на объект thread_local, который я хочу прочитать.

С помощью «referer» я ссылаюсь на то, что управляет или может дать вам доступ к своей переменной thread_local из его потока. Но это невозможно! Любое выражение, дающее переменную thread_local, независимо от того, кто (я сделал другой тест, даже с функциями доступа), заканчивается использованием экземпляра thread_local потока чтения.

+1

Я не уверен, что использование потока «thread_local» для обмена данными между потоками соответствует духу хранилища «thread_local». Почему бы просто не использовать обычный «статический»? – Galik

+0

Я просто ... «понимая поведение» (или пытаюсь). –

+1

А ну его синтаксис * * проблема. Просто потому, что вы используете синтаксис, который выглядит так, будто вы разыскиваете указатель 'A', это не так. Компилятор видит, что он является «статическим» и игнорирует указатель и идет прямо для одного (для каждого потока в этом случае) статического экземпляра. Весь бизнес 'thread_local' на самом деле не имеет к этому отношения. – Galik

ответ

6

Это вопрос синтаксиса: В этом случае a->i; является идентичен к A::i; потому, что A::i является статическим членом и его адрес не зависит от любого одного экземпляра A.

Итак, просто потому, что вы используете синтаксис, который выглядит так, будто вы разыгрываете указатель A, а вы нет. Компилятор видит, что это статический член и игнорирует указатель и идет прямо для единственного (в данном случае в данном случае) static экземпляра. Весь бизнес thread_local на самом деле не имеет отношения к этому.

Итак, когда вы обращаетесь к статическому члену из A через A* a в вашем лямбды, компилятор игнорирует адрес вы дали и делать A::i независимо (получение своей собственную thread_local версии).

struct A 
{ 
    static int i; 
}; 

A* a = new A; 
a->i; // identical to A::i (because i is static) 

Это стандартный синтаксис, как указано здесь, в C++ 14 Standard:

5.2.5 доступ к члену класса [ expr.ref ]

1. Постфиксное выражение, за которым следует точка. или стрелка ->, необязательно сопровождаемая шаблоном ключевого слова (14.2), , а затем последующее выражение id, является постфиксным выражением. Вычисляется постфиксное выражение перед точкой или стрелкой ; результат этой оценки вместе с id-выражением определяет результат полного постфиксного выражения .

...

65) Если выражение доступа члена класса оцениваются, оценка Подвыражения происходит, даже если результат является ненужным , чтобы определить значение всего выражения постфикса, например , если идентификатор-выражение обозначает статический элемент.

(курсив мой)

+0

Но это «компилятор»? Или он присутствует на стандарте? –

+0

@ Peregring-lk Его часть синтаксиса. Если вы думаете об этом, существует только ** одна ** версия статических членов для каждого класса. Итак, как компилятор может решить, к какому адресу вы обращаетесь к этому ** одиночному ** экземпляру? С 'thread_local' это просто означает, что есть только один * для потока *, но проблема остается. Доступ к * static * членам через '->' не использует указатель, он просто использует тип * type * для поиска статического (или thread_local static) экземпляра. – Galik

+0

Да. мой комментарий удален как устаревший. Вскоре это произойдет. – Yakk

0

вы прошли один «A» porinter, но мы должны знать, что «я» переменной и переменной «P_A» не принадлежит «А» на самом деле, то статические, так что, хотя вы начали поток, переданный указателем «A», а затем изменили переменную «i», это другое, потому что это «i» не находится за пределами «i», они разные.