C++ 线程\局部变量的实例是否保证由访问它们的线程初始化?

C++ 线程\局部变量的实例是否保证由访问它们的线程初始化?,c++,multithreading,c++17,language-lawyer,c++20,C++,Multithreading,C++17,Language Lawyer,C++20,“所有具有线程本地存储持续时间的非本地变量都将作为线程启动的一部分进行初始化,并在线程函数开始执行之前进行排序。”() 但是,cppreference似乎没有提到这些变量实际上是由正在启动的线程初始化的,还是由正在启动新线程的线程初始化的。 考虑下面的例子: (id.hpp) 如果我从一个不是主线程的线程访问这个线程\u id,我会得到该线程的id吗?如果此线程id是“作为线程启动的一部分”初始化的,那么它也可能是由原始调用线程初始化的,因此此线程id的值不正确 我知道有一些设计模式可以完全避免

“所有具有线程本地存储持续时间的非本地变量都将作为线程启动的一部分进行初始化,并在线程函数开始执行之前进行排序。”()
但是,cppreference似乎没有提到这些变量实际上是由正在启动的线程初始化的,还是由正在启动新线程的线程初始化的。
考虑下面的例子:

(id.hpp)

如果我从一个不是主线程的线程访问
这个线程\u id
,我会得到该线程的id吗?如果
此线程id
是“作为线程启动的一部分”初始化的,那么它也可能是由原始调用线程初始化的,因此
此线程id
的值不正确


我知道有一些设计模式可以完全避免这个问题,但这就违背了这个问题的全部要点,所以我不想在这里使用它们。

作为一个线程本地,每个线程都有一个副本,所以每个线程都有不同的值

至于初始化的时间:

在线程初始函数的第一条语句之前排序,或延迟。 如果延迟,则在t首次非初始化odr使用任何非内联变量之前,对与线程t的实体相关联的初始化进行排序,该变量的线程存储持续时间定义在与待初始化变量相同的转换单元中

因此,它在每个线程中初始化,无论是在运行线程函数之前还是在使用之前

我编写了一个测试:

#include <thread>
#include <iostream>

namespace m {
    std::thread::id get_id () {
        std::cout << "My get id called." << std::endl;
        return std::this_thread::get_id();
    }
}

thread_local const std::thread::id this_thread_id = m::get_id();

int main ()
{
    std::cout << "Main: " << this_thread_id << std::endl;
    
    std::thread th2 (
        [&]{
            uint64_t i {0};
            std::cout << "Thread.int: " << i << std::endl;
            std::cout << "Here it could be uninitialized." << std::endl;
            std::cout << "Thread .2: " << this_thread_id << std::endl;
        });

    th2.join ();
    
    std::cout << "End" << std::endl;
    
    return 0;
}

因此,在
g++
中,它似乎是在首次使用时初始化的。即使您只访问它所在的内存。

Huh,也没有想到:“是否在[…]之前对具有线程存储持续时间的非块非内联变量进行动态初始化,这是实现定义的。”它是一种实现定义,在程序中的哪个线程和哪个点上发生延迟的动态初始化。。。当然也不是我所期望的,谢谢你的评论!用当前线程id初始化线程变量有什么意义?如果需要,那么直接调用
std::this\u thread::get\u id()
。一个无用的例子也反驳了问题的关键。我试图找出一些简短的例子,根据哪个线程执行初始化,初始化会产生不同的结果。我想这会让问题更清楚-如果没有帮助,我很抱歉。一个实验不足以证明这一保证。但是我们需要保证可靠地使用特定于线程的初始化。。。
#include "id.hpp"
thread_local const std::thread::id this_thread_id = std::this_thread::get_id();
#include <thread>
#include <iostream>

namespace m {
    std::thread::id get_id () {
        std::cout << "My get id called." << std::endl;
        return std::this_thread::get_id();
    }
}

thread_local const std::thread::id this_thread_id = m::get_id();

int main ()
{
    std::cout << "Main: " << this_thread_id << std::endl;
    
    std::thread th2 (
        [&]{
            uint64_t i {0};
            std::cout << "Thread.int: " << i << std::endl;
            std::cout << "Here it could be uninitialized." << std::endl;
            std::cout << "Thread .2: " << this_thread_id << std::endl;
        });

    th2.join ();
    
    std::cout << "End" << std::endl;
    
    return 0;
}
manuel@desktop:~/projects/soanswers/src (master)$ g++ ini.cc -o ini -std=c++2a -pthread && ./ini
Main: My get id called.
140064769697600
Thread.int: 0
Here it could be uninitialized.
Thread .2: My get id called.
140064769693440
End