Capturing a thread_local in a lambda:
#include <iostream>
#include <thread>
#include <string>
struct Person
{
std::string name;
};
int main()
{
thread_local Person user{"mike"};
Person& referenceToUser = user;
// Works fine - Prints "Hello mike"
std::thread([&]() {std::cout << "Hello " << referenceToUser.name << std::endl;}).join();
// Doesn't work - Prints "Hello"
std::thread([&]() {std::cout << "Hello " << user.name << std::endl;}).join();
// Works fine - Prints "Hello mike"
std::thread([&user=user]() {std::cout << "Hello " << user.name << std::endl;}).join();
}
https://godbolt.org/z/zeocG5ohb
It seems like if I use the original name of a thread_local then its value on the thread which executes the lambda is the thread_local version of the thread which is running the lambda. But as soon as I take a reference or pointer to the thread local it turns into (a pointer to) the originating threads instance.
What are the rules here. Can I rely on this analysis?
Similar to local
staticobjects, localthread_local(implicitlystatic thread_local) objects are initialized when control passes through their declaration for the first time.The thread you are creating never executes
main, only the main thread does, so you're accessinguserbefore its lifetime has begun on the extra thread.Your three cases explained
We are capturing
referenceToUserwhich refers to theuseron the main thread. This is okay.We are accessing
useron the extra thread before its lifetime has begun. This is undefined behavior.Once again, we are referencing the
userfrom the main thread here, which is same as the first case.Possible fix
If you declare
useroutside ofmain, thenuserwill be initialized when your thread starts, not whenmainruns:Alternatively, declare
userinside of your lambda expression.Note: It's not necessary to capture
thread_localobjects. The second example could be[] { ... }.