【问题标题】:thread_local variable not consistent within a threadthread_local 变量在线程内不一致
【发布时间】:2020-02-04 18:46:50
【问题描述】:

我在文件 tracker.hpp 中有一个变量:

namespace TRIALS
{
    static thread_local int a = -1;
}

我在 ema.hpp/ema.cpp 文件中有另一个名为 EMP 的类

namespace Algo
{
    class EMP
    {
        public:
            void Sample();
    };
}
namespace Algo
{
    void EMP::Sample()
    {
        std::cout << "model " << std::this_thread::get_id() << " " << &TRIALS::a << " " << TRIALS::a << std::endl;
    }
}

然后我有我的主文件


auto model = Algo::EMP();

void Simulate(const int a)
{
    TRIALS::a = a;
    model.Sample()
    std::cout << "worker " << std::this_thread::get_id() << " " << &TRIALS::a << " " << TRIALS::a << std::endl;
}

int main()
{
    std::cout << &TRIALS::a << std::endl;
    const int nthreads = 1;

    std::cout << "main " << std::this_thread::get_id() << " " << &TRIALS::a << " " << TRIALS::a << std::endl;

    std::vector<std::thread> threads;
    for(int i=0; i<nthreads; ++i)
    {
        threads.emplace_back(&Simulate, i);
    }

    for(auto &thread : threads)
    {
        thread.join();
    }

    std::cout << "main " << std::this_thread::get_id() << " " << &TRIALS::a << " " << TRIALS::a << std::endl;

    return 0;
}

我只是运行一个线程进行调试,但这是输出:

0x7f9540b621d8

main 140279012532800 0x7f9540b621d8 -1(如预期)

model 140278985606912 0x7f953f1b469c -1(不应该是0吗??)

worker 140278985606912 0x7f953f1b4698 0(如预期)

main 140279012532800 0x7f9540b621d8 -1(如预期)

我的印象是每个线程都有自己的 TRIALS::a 本地副本。模型中的 a 正确地增加了,但是当它从同一个线程中的函数返回时,该值仍然为 0。我正在打印出线程 ID 和 a 的地址,我看到实际上有 3 个不同的TRIALS::a 的版本,尽管总共只有两个线程。

作为附加问题,static thread_local int athread_local int a 之间有什么区别?

【问题讨论】:

  • 你知道static变量是什么吗?
  • 你使用什么样的编译器/标准库/目标操作系统。本地线程实际上是特定于运行时的东西。
  • 我正在使用 gcc 7.4 和 ubuntu 18.04
  • 每个.cpp 文件都有自己的副本。您需要 extern 而不是 static 在文件之间共享全局。
  • @john 相反。 static 这里的意思是“不是全球性的”。看到这个:stackoverflow.com/questions/14349877/…static 很奇怪并且依赖于上下文。最糟糕的 C++ 结构之一。

标签: c++ multithreading thread-local


【解决方案1】:

在您的示例中,static 使 thread_local 对象使用内部链接,因此每个翻译单元(.cpp 文件)都有自己的变量副本。

详情请见storage class specifiers

thread_local 关键字仅允许用于在命名空间范围内声明的对象、在块范围内声明的对象和静态数据成员。它表示对象具有线程存储持续时间。 可与staticextern组合,分别指定内部或外部链接(静态数据成员始终具有外部链接除外),但附加的静态不影响存储时长。强>

即您可能希望删除 static 关键字,以便在整个应用程序中只有一个对象副本。在头文件中做:

namespace TRIALS {
    extern thread_local int a;
}

.cpp 之一中:

thread_local int TRIALS::a = -1;

在 C++17 中,您可以创建变量 inline 以避免在 .cpp 中提供其定义:

namespace TRIALS {
    inline thread_local int a = -1;
}

【讨论】:

  • 如果我放弃静态,编译会抱怨 TRIALS::a 有多个定义,这是有道理的,因为我将文件包含在 ema.hpp 和 main.cpp 中
  • @john 这就是extern 发挥作用的时候。
  • @john 为您添加了一个示例。
  • 哇太棒了!谢谢@MaximEgorushkin。看来我必须重新学习静态和外部
  • 谢谢@freakish 我将不得不重新学习静态和外部。感谢您为我指明正确的方向
猜你喜欢
  • 2013-02-19
  • 2023-02-14
  • 1970-01-01
  • 1970-01-01
  • 2019-12-31
  • 2017-08-22
  • 2017-09-11
  • 2014-05-08
  • 2018-02-01
相关资源
最近更新 更多