我一直在考虑延迟初始化(让我们说 - 全局变量,但它可以是任何东西)。到目前为止,我提出的是类似
的内容enum state {
uninitialized,
initializing,
initialized
};
state s;
char memory[sizeof(T)];
T& initialize() {
auto val = compare_and_swap(&s, state::uninitialized, state::initializing);
if (val == initialized)
return *(T*)memory;
if (val == initializing) {
while(atomic_read(&s) != state::initialized);
return *(T*)memory;
}
new (memory) T();
atomic_write(&s, state::initialized);
return *(T*)memory;
}
如果它已经被初始化,那么它就是等待的。但是我遇到了一个线程正在初始化的问题。完成初始化或等待初始化完成所需的步骤数与线程数不成比例。但是如果初始化线程暂停,其他线程都必须随意等待直到它恢复。因此,在一般情况下,它不是无锁或无等待。
是否可以创建等待无锁或无锁的延迟初始化?
答案 0 :(得分:3)
如果您愿意初始化多个对象,那么您可以通过仅存储指针来生成无锁代码:
std::atomic<T *> p { nullptr };
T & get()
{
T * q = p.load();
if (!q)
{
T * r = new T;
if (p.compare_exchange_strong(q, r))
{
return *r;
}
else
{
delete r;
return *q;
}
}
return *q;
}
无锁算法的成本是您通常必须尝试失败&#34;,因此即使您必须丢弃结果,您也必须付出本地尝试的代价。
正如您正确指出的,如果只有一个线程执行初始化,那么您总是依赖于该单个线程,并且您无法锁定。
如果析构函数有副作用,您还需要相应的清理代码:
delete p.exchange(nullptr);
答案 1 :(得分:0)
我不认为在一般情况下是可能的。如果您在全局初始化过程中执行此操作,则在调用main之前和创建线程之前,您有机会。它如何等待或无锁?
如果您允许初始化失败并稍后重试,则它可以是无锁的。 如果另一个线程有项目,它就不能等待。
如果你关心这里的性能,那么你可以使用线程id散列到一个项目桶中,以尝试减少争用的频率。