Search code examples
c++multithreadingthread-local

thread_local variable not consistent within a thread


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 ?


Solution

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