块作用域的 thread_local

thb*_*thb 6 c++ multithreading thread-local-storage

thread_local在块范围内使用变量有什么用?

如果一个可编译的示例有助于说明问题,这里是:

#include <thread>
#include <iostream>

namespace My {
    void f(int *const p) {++*p;}
}

int main()
{
    thread_local int n {42};
    std::thread t(My::f, &n);
    t.join();
    std::cout << n << "\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出: 43

在示例中,新线程有自己的,n但(据我所知)不能用它做任何有趣的事情,所以为什么要麻烦呢?新线程自己的n有什么用吗?如果它没有用,那又有什么意义呢?

当然,我认为有一个点。我只是不知道这有什么意义。这就是我问的原因。

如果新线程自己n想要(如我所想)在运行时由 CPU 进行特殊处理——也许是因为,在机器代码级别,人们无法n通过从新线程的基指针的预先计算的偏移量以正常方式访问自己的线程堆栈——那我们岂不是白白浪费机器周期和电力?然而即使不需要特殊处理,仍然没有收获!不是我能看到的。

那么为什么thread_local在块范围内?

参考

Cru*_*ean 4

我发现thread_local只在三种情况下有用:

  1. 如果您需要每个线程都有一个唯一的资源,这样它们就不必共享、互斥等来使用所述资源。即便如此,这仅在资源很大和/或创建成本昂贵或需要跨函数调用持续存在时才有用(即函数内的局部变量不够)。

  2. (1) 的一个分支 - 当调用线程最终终止时,您可能需要运行特殊的逻辑。为此,您可以使用thread_local在函数中创建的对象的析构函数。对于进入带有声明的代码块的每个线程(在线程生命周期结束时),此类对象的thread_local析构函数都会被调用一次。thread_local

  3. 您可能需要为调用它的每个唯一线程执行一些其他逻辑,但仅执行一次。例如,您可以编写一个函数来注册调用函数的每个唯一线程。这可能听起来很奇怪,但我发现它可以用于管理我正在开发的库中的垃圾收集资源。该用法与(1)密切相关,但在其构造后不再使用。实际上是线程整个生命周期的哨兵对象。