我最近使用的对象的目的是将内存分配和释放为单例。像
这样的东西class MyValue
{
// ...
static Allocator& GetAllocator()
{
static Allocator allocator;
return allocator;
}
// ...
};
后来我意识到Allocator
不是线程安全的:当多个线程同时使用相同的分配器时,偶尔会发生奇怪的事情,导致断言和分段错误。
解决方案:为不同的线程使用不同的分配器:
class MyValue
{
// ...
static Allocator& GetAllocator()
{
thread_local static Allocator allocator;
return allocator;
}
// ...
};
真棒!我的问题不见了!只有一个问题: 每次创建一个线程时,我的allocator变量是否会被初始化,即使大多数线程不使用这个变量?
分配器的初始化可能是繁重的操作,所以我希望它只在实际需要时进行初始化,而不是在每个线程中进行初始化。
我读到每个线程都有thread_local
个变量分配。这是否意味着它们也是构造的?对于每个创建的线程,还是仅为使用它的线程系统地发生这种分配(或构造)?
我依旧记得在课程中听到有关线程和线程本地存储的大多数细节都与平台有关。如果是这种情况,我对Linux和FreeBSD特别感兴趣。
相关(有趣的读物,但我找不到答案):
答案 0 :(得分:3)
[basic.stc.thread]陈述
使用
thread_local
关键字声明的所有变量都具有线程存储持续时间。这些实体的存储应持续创建它们的线程的持续时间。每个线程都有一个不同的对象或引用,声明名称的使用是指与当前线程关联的实体。- 醇>
具有线程存储持续时间的变量应在其第一次使用(6.2)之前初始化,如果构造,则应在线程退出时销毁。
因此,您将获得每个线程中对象的存储空间。我们还有[stmt.dcl] / 4表明
第一次控件通过其声明时,将执行具有静态存储持续时间(6.7.1)或线程存储持续时间(6.7.2)的块范围变量的动态初始化;这样的变量在初始化完成后被认为是初始化的。
因此,如果我们到达声明,那么对象将被初始化,如果它有一个构造函数,它将被调用。
如果我们把所有这些放在一起你将会有一些构造函数和析构函数调用等于实际到达的线程数
thread_local static Allocator allocator;
答案 1 :(得分:0)
我可以为您提供一个解决方案,以便在每次调用Allocator
时检查是否创建了GetAllocator()
类型的新对象。只需调用此方法至少5次,并检查所有对象返回的地址。如果所有返回对象的地址不同,那么它会在每次调用中创建不同的对象,或者如果不是,则每次调用GetAllocator()
时都会返回相同对象的地址。