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 static
objects, local thread_local
(implicitly static 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 accessing user
before its lifetime has begun on the extra thread.
std::thread([&]() {std::cout << "Hello " << referenceToUser.name << std::endl;}).join();
We are capturing referenceToUser
which refers to the user
on the main thread. This is okay.
std::thread([&]() {std::cout << "Hello " << user.name << std::endl;}).join();
We are accessing user
on the extra thread before its lifetime has begun. This is undefined behavior.
std::thread([&user=user]() {std::cout << "Hello " << user.name << std::endl;}).join();
Once again, we are referencing the user
from the main thread here, which is same as the first case.
If you declare user
outside of main
, then user
will be initialized when your thread starts, not when main
runs:
thread_local Person user{"mike"};
int main() {
// ...
Alternatively, declare user
inside of your lambda expression.
Note: It's not necessary to capture thread_local
objects. The second example could be [] { ... }
.