thread_local與__thread的區別

gcc版本爲(gcc version 7.3.0 (Debian 7.3.0-19))

引言

兩個關鍵字都是關於線程存儲的,不過一個是C語言的,一個是C++11的特性。它們之間有什麼區別呢?因爲在CSDN沒有找到解答,遂在解答中對過程進行記錄,以幫助有同樣疑惑的同學。

過程

__thread is supported on GNU, clang and more. It was available before thread_local… they are not equivalent and both are supported. the difference is that thread_local uses lazy initialization to initialize the variable in only threads that access it. __thread does not initialize at all and you must manually initialize it per thread. thread_local thus has an overhead per access and __thread does not. Apple’s compilers disable thread_local and not thread because of this inefficiency, Although __thread is not available on all compilers, __thread is available with GNU tools

__thread被GNU支持,clang等支持.它在thread_local之前是很有用的…它們之間並不相等,而且一般都被支持.它們之間的不同就是thread_local在訪問變量的線程中使用延遲初始化來初始話變量.而__thread並不初始化,您必須手動初始化.所以每一次thread_local都是有開銷的,而__thread沒有.因爲這種低效率,Apple的編譯器禁用thread_local而不禁用thread,儘管__thread不是在所有編譯器上都可用,但_其在GNU工具中可用.(thread_local是C++11的特性)

我們可以看到區別就是延遲初始化帶來的效率上的區別,這裏的的開銷指的是什麼呢?開銷就是 thread_local 變量的每次使用都將成爲一個函數調用。這讓人感到不可思議。

這裏的過程可以查看參考[4],因爲C++生成的彙編代碼實在是過於複雜,我在我機子上生成彙編以後看了半天沒看出來,所以大家直接查看參考即可,那篇文章還是比較詳細的。

看到有些地方說__thread不支持class相關的構造,析構,我們寫一個代碼看看:

#include <bits/stdc++.h>
using namespace std;

class local_{
    public:
    local_(){
        cout << "hello world\n";
    }
    void show(){
        cout << "hello world1\n";
    }
    ~local_(){
        cout << "hollo world2\n";
    }
};

thread_local local_ Temp;
//__thread local_ TT;

void test(){
    thread_local local_ T;
    //static __thread local_ T;
    Temp.show();
}

int main(){
    auto T = std::thread(&test);
    T.join();
    cout << "T\n";
    return 0;
}

輸出:

hello world
hello world
hello world1
hollo world2
hollo world2

沒有什麼問題,線程局部變量和全局的Temp都經歷了一次構造和析構。

那麼如果使用__thread的話呢?我們註釋掉thread_local local_ Temp這一句,使用__thread,得到這樣的結果:

non-local variable ‘TT’ declared ‘__thread’ needs dynamic initialization

需要動態初始化,__thread是不支持非函數本地變量的。那麼如果讓它跑起來呢,我們執行動態初始化就可以了,我們需要把代碼修改成如下:

static __thread local_* Temp = nullptr;

void test(){
    //thread_local local_ T;
    //static __thread local_ T;
    Temp = new local_();
    Temp->show();
    delete Temp;
}

輸出爲:

hello world
hello world1
hollo world2

這樣就可以了,沒有什麼問題。

如果我們把線程局部的thread_local換成__thread呢,我們來看看會發生什麼:

hello world
hello world
hello world1
hollo world2
hollo world2

沒有什麼問題。仍然會發生構造和析構,所以傳言被打破了。

結論

通過以上測試與查閱資料,得出以下結論。

  1. 執行階段的效率問題。__thread不會發生函數調用,而thread_local對變量的一次訪問就是一次函數調用
  2. __thread在某些平臺上支持,比如GNU,clang等,但是有些平臺不支持。thread_local作爲C++11的特性,基本都被支持
  3. __thread在實現非函數本地變量的時候需要手動初始化,而thread_local延遲綁定,不需要我們初始化,帶來的代價就是第一條
  4. 兩個關鍵字都支持類的構造與析構

作者水平有限,有問題的地方還請大家給出寶貴的建議。

參考:

  1. 博文《linux編程 - C/C++每線程(thread-local)變量的使用
  2. 問答《Using __thread in c++0x :stackoverflow
  3. 文檔《Thread-Local Storage :文檔
  4. 博文《C ++ 11:GCC 4.8 thread_local 性能懲罰?(C++11: GCC 4.8 thread_local Performance Penalty?)
  5. 博文《c++ 線程局部變量thread_local
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章