相关疑难解决方法(0)

linux pthreads上的gcc 4.7 - 使用__thread的非常重要的thread_local解决方法(无提升)

在C++ 11中,您可以使用thread_local存储创建一个非平凡的对象:

class X { ... }

void f()
{
    thread_local X x = ...;
    ...
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这个功能尚未在gcc中实现(截至4.7).

gcc允许你拥有线程局部变量,但只允许使用普通类型.

我正在寻找一个解决方法:

这是我到目前为止:

#include <iostream>
#include <type_traits>

using namespace std;

class X
{
public:
    X() { cout << "X::X()" << endl; };
    ~X() { cout << "X::~X()" << endl; }
};

typedef aligned_storage<sizeof(X), alignment_of<X>::value>::type XStorage;

inline void placement_delete_x(X* p) { p->~X(); }

void f()
{
        static __thread bool x_allocated = false;
        static __thread XStorage x_storage;

        if (!x_allocated)
        {
                new (&x_storage) X;
                x_allocated …
Run Code Online (Sandbox Code Playgroud)

c++ linux multithreading pthreads c++11

8
推荐指数
1
解决办法
3634
查看次数

为什么 gcc 不消除对 `_tls_get_addr()` 的连续调用?

我有使用 thread_local 缓冲区的代码,类似于:

int func() {
    thread_local std::vector<int> buffer;

    buffer.resize(0);
    for (int i = 0; i < 10000; i++) {
        buffer.push_back(i);
    }

    return processing(buffer);
}
Run Code Online (Sandbox Code Playgroud)

在分析我的代码时,我注意到 gcc 在循环体内调用了一个调用_tls_get_addr(),以便访问buffer. 循环体的Godbolt反汇编如下所示:

        lea     rbx, -20[rbp]
        data16  lea rdi, f()::buffer@tlsgd[rip]
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT  ; <- This call!
        mov     rsi, rbx
        mov     rdi, rax
        call    std::vector<int, std::allocator<int> >::push_back(int const&)@PLT
Run Code Online (Sandbox Code Playgroud)

这些调用大大减慢了循环速度。我可以手动使用此版本并参考:

        lea     rbx, -20[rbp]
        data16  lea rdi, f()::buffer@tlsgd[rip]
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT  ; <- This call!
        mov …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading gcc thread-local-storage

5
推荐指数
0
解决办法
439
查看次数

线程局部变量的初始化

我最近使用了一个对象,其目的是将内存作为单例进行分配和释放。就像是

class MyValue
{
  // ...

  static Allocator& GetAllocator()
  {
    static Allocator allocator;
    return allocator;
  }

  // ...
};
Run Code Online (Sandbox Code Playgroud)

后来我意识到它Allocator不是线程安全的:当多个线程同时使用同一个分配器时,偶尔会发生奇怪的事情,导致断言和分段错误。

解决方案:为不同的线程使用不同的分配器:

class MyValue
{
  // ...

  static Allocator& GetAllocator()
  {
    thread_local static Allocator allocator;
    return allocator;
  }

  // ...
};
Run Code Online (Sandbox Code Playgroud)

惊人的!我的问题都消失了!只有一个问题: 每次创建线程时我的分配器变量都会被初始化,即使大多数线程不会使用该变量?

分配器的初始化可能是繁重的操作,因此我希望仅在实际需要时才对其进行初始化,而不是在每个线程中进行初始化。

我读到thread_local变量是由每个线程分配的。这是否意味着它们也是被构建的?这种分配(或构造)是针对创建的每个线程系统地发生还是仅针对使用它的线程发生?

我依稀记得在课程中听说过有关线程和线程本地存储的大多数细节都依赖于平台。如果是这样的话,我对 Linux 和 FreeBSD 特别感兴趣。


相关(有趣的读物,但我在那里找不到答案):

c++ multithreading thread-local-storage c++11

3
推荐指数
1
解决办法
6023
查看次数