5

I have a variable in file tracker.hpp:

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

I have another class in file called EMP in ema.hpp/ema.cpp

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;
    }
}

Then my main file I have


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;
}

I am just running one thread for debugging but this is the output:

0x7f9540b621d8

main 140279012532800 0x7f9540b621d8 -1 (As expected)

model 140278985606912 0x7f953f1b469c -1 (Shouldn't this be 0??)

worker 140278985606912 0x7f953f1b4698 0 (As expected)

main 140279012532800 0x7f9540b621d8 -1 (As expected)

I was under the impression that each thread has it's own local copy of TRIALS::a. The a in model correctly gets incremented but when it returns from the function in the same thread, the value is still 0. I am printing out the thread ids and the address of a for good measure and I am seeing that there are actually 3 different versions of TRIALS::a despite only two total threads.

As a bonus question, what is the difference between static thread_local int a and thread_local int a ?

11
  • 1
    Do you know what static variable is? Commented Oct 7, 2019 at 13:05
  • What kind of compiler/standard library/target operating system do you use. Thread local is really run-time specific thing. Commented Oct 7, 2019 at 13:06
  • i'm using gcc 7.4, with ubuntu 18.04 Commented Oct 7, 2019 at 13:07
  • 3
    Each .cpp file gets its own copy. You need extern instead of static to share a global between files. Commented Oct 7, 2019 at 13:10
  • 2
    @john on the contrary. static here means "not global". See this: stackoverflow.com/questions/14349877/… static is weird and context dependent. One of the worst C++ constructs. Commented Oct 7, 2019 at 13:21

1 Answer 1

8

In your example static makes that thread_local object use internal linkage, so that each translation unit (.cpp file) has its own copy of the variable.

See storage class specifiers for details:

The thread_local keyword is only allowed for objects declared at namespace scope, objects declared at block scope, and static data members. It indicates that the object has thread storage duration. It can be combined with static or extern to specify internal or external linkage (except for static data members which always have external linkage), respectively, but that additional static doesn't affect the storage duration.

I.e. you may like to drop that static keyword, so that you only have one copy of the object in the entire application. In the header file do:

namespace TRIALS {
    extern thread_local int a;
}

And in one of the .cpp:

thread_local int TRIALS::a = -1;

In C++17, you can make the variable inline to avoid having to provide its definition in a .cpp:

namespace TRIALS {
    inline thread_local int a = -1;
}
Sign up to request clarification or add additional context in comments.

6 Comments

If I drop the static, compiling complains that there are multiple definitions of TRIALS::a which makes sense since I am including the file in ema.hpp and main.cpp
@john that's when extern comes to play.
@john Added an example for you.
wow awesome! thanks @MaximEgorushkin. Looks like I have to relearn static and extern
@john don't worry, it took me years to fully understand this s**t. Good luck!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.