2016-09-18 103 views
1

讓我們先從一張code (Coliru)線程局部變量

#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它的線程本地副本,即使我訪問它來自另一個線程的另一個線程本地對象。這是預期的行爲嗎?因爲除非我將指針或引用傳遞給我想要讀取的外部thread_local對象,否則無法使用「引用者」從另一個線程獲取信息。

使用「referer」,我引用管理或可以讓你從其線程訪問自己的thread_local變量的內容。但這是不可能的!任何表達式產生一個thread_local變量,無論從哪個人(我做了不同的測試,甚至是訪問函數),都通過使用讀線程的thread_local實例結束。

+1

我不確定使用'thread_local'在線程之間進行通信符合'thread_local'存儲的精神。爲什麼不使用普通的'static'? – Galik

+0

我只是....「瞭解行爲」(或試圖)。 –

+1

啊,它的一個*語法*問題。僅僅因爲你正在使用的語法看起來像是取消引用'A'指針,你不是。編譯器認爲它是一個'static'並忽略指針,並直接針對單個(在這種情況下是每個線程)靜態實例。整個'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標準提到的標準語法:

5.2.5類的成員訪問[ expr.ref ]

1.後綴表達式後跟一個點。或一個箭頭 - >,可選地跟隨關鍵字模板(14.2), ,然後是一個id-表達式,是一個後綴表達式。評估點或箭頭 之前的後綴表達式; 該評估結果與id表達式一起決定了整個後綴表達式的結果。

...

65)如果類成員訪問表達式,子表達式評估發生即使結果是不必要 確定整個後綴表達式的值,例如如果ID-表達表示一個靜態成員

(重點煤礦)

+0

但這是一個「編譯器」的東西?或者它是否存在於標準中? –

+0

@ Peregring-lk它的語法的一部分。如果你考慮一下,每個類只有一個**版本的靜態成員。那麼編譯器如何關心你訪問那個**單個**實例的地址呢?使用'thread_local'它僅僅意味着每個線程只有一個*但問題仍然存在。通過' - >'訪問* static *成員不使用指針,它只是使用*類型*來查找靜態(或thread_local靜態)實例。 – Galik

+0

是的。我的評論被刪除爲過時。這一個會很快跟進。 – Yakk

0

你傳遞一個「A」 porinter,但我們應該知道,「我」變量和「P_A」變量不屬於「A」實際上,然後是靜態的,所以雖然你開始了一個由'A'指針傳遞的線程,然後修改'i​​'變量,這是不同的,因爲'i'不在'i'之外,它們是不同的。