【问题标题】:Thread local variable accessed through non-thread local object通过非线程局部对象访问的线程局部变量
【发布时间】:2016-09-18 11:57:14
【问题描述】:

让我们从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 的线程本地副本,即使我从另一个线程的另一个线程本地对象访问它。这是预期的行为吗?因为除非我将指针或引用传递给要读取的外部线程本地对象,否则无法使用“引用者”从另一个线程获取信息。

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

【问题讨论】:

  • 我不确定使用thread_local 在线程之间进行通信是否符合thread_local 存储的精神。为什么不直接使用普通的static
  • 我只是....“理解行为”(或试图)。
  • 嗯,它是一个语法问题。仅仅因为您使用的语法看起来像是在取消引用 A 指针,所以您不是。编译器看到它是static 并忽略指针并直接进入单个(在这种情况下为每个线程)静态实例。整个thread_local 业务实际上与此无关。
  • 使 i 成为常规的非静态和非 thread_local 成员。然后你的 lambda 将引用 main 中创建的 A 实例中的 i。

标签: c++ multithreading c++11 thread-local


【解决方案1】:

这是一个语法问题:在这种情况下,a-&gt;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. 后缀表达式后跟一个点。或箭头 ->,可选地后跟关键字模板 (14.2), 然后是一个 id 表达式,是一个后缀表达式。点或箭头前的后缀表达式 被评估; 65 该评估的结果与 id-expression 一起确定 整个后缀表达式。

...

65) 如果评估类成员访问表达式,即使结果是不必要的,也会发生子表达式评估 确定整个后缀表达式的值,例如如果 id 表达式表示静态成员

(强调我的)

【讨论】:

  • 但这是一个“编译器”的东西?或者它是否存在于标准中?
  • @Peregring-lk 它是语法的一部分。如果您考虑一下,每个类只有 一个 版本的静态成员。那么编译器如何关心您访问那个 single 实例的地址呢?对于thread_local,它仅仅意味着每个线程只有一个,但问题仍然存在。通过-&gt; 访问static 成员不使用指针,它只是使用class 类型 来查找静态(或thread_local 静态)实例。
  • 是的。我的评论已过时而被删除。这个很快就会出现。
  • 是的,它更清晰。我只是想知道您是否因为“自己的扣除”而回答,或者那是“设计”的行为。认为取消引用运算符被忽略;它只是用来跟踪表达式所指的对象;这就像搜索“按名称”。认为“p_a”可能是一个定制的指针包装器,带有重载的operator* 等等; “语法糖”解释将更难辩护(如果在返回参考之前涉及if 怎么办?)。但无论如何,无论如何,我确信使用的 var 将是线程本地的,而不是外部的。
  • @Peregring-lk 我刚刚包含了来自C++14 Standard 的摘录,确认此语法在访问静态成员时是合法的。关于重载operator-&gt;,这只会产生另一个指针,最终应用-&gt;。我想(虽然没有确认)产生的指针的类型决定了在这种情况下访问哪个类的静态成员。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-16
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    相关资源
    最近更新 更多