通过非线程局部对象访问的线程局部变量

Thread local variable accessed through non-thread local object

本文关键字:访问 线程 局部变量 对象 线程局      更新时间:2023-10-16

让我们从一段代码(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的线程本地副本,即使我是从另一个线程的另一个线程本地对象访问它。这是预期行为吗?因为使用"referer"从另一个线程获取信息是不可能的,除非我传递一个指针或引用到我想要读取的外部thread_local对象。

使用"referer",我指的是管理或可以让您从其线程访问其自己的thread_local变量的东西。但那是不可能的!!任何产生thread_local变量的表达式,无论来自谁(我做了不同的测试,甚至使用了访问器函数),都以使用读线程的thread_local实例结束。

这是一个语法问题:在这种情况下,a->i;A::i;相同,因为A::i静态成员,其地址不依赖于A的任何一个实例。

所以仅仅因为你使用的语法看起来像你在解引用一个A指针,你不是。编译器看到它是一个静态成员,忽略指针,直接处理单个(在这种情况下是每个线程)static实例。整个thread_local业务实际上与此无关。

因此,当您在lambda中访问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。一个后缀表达式后面跟一个点。或者一个箭头->,可选后跟关键字template (14.2),然后跟一个id表达式,是一个后缀表达式。点或箭头前的后缀表达式评估;65该求值的结果与id-表达式一起决定整个后缀表达式

65)如果对类成员访问表达式求值,则即使结果是不必要的,也会对子表达式求值确定整个后缀表达式的值,例如,如果id-expression表示静态成员,则使用

(强调我的)

你传递了一个'A'指针,但我们应该知道'i'变量和'p_a'变量实际上不属于'A',然后是静态的,所以虽然你启动了一个线程,通过'A'指针传递,然后修改'i'变量,它是不同的,因为这个'i'不在'i'之外,它们是不同的