无等待或无锁初始化

时间:2014-02-08 17:44:28

标签: c++

我一直在考虑延迟初始化(让我们说 - 全局变量,但它可以是任何东西)。到目前为止,我提出的是类似

的内容
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;
}

如果它已经被初始化,那么它就是等待的。但是我遇到了一个线程正在初始化的问题。完成初始化或等待初始化完成所需的步骤数与线程数不成比例。但是如果初始化线程暂停,其他线程都必须随意等待直到它恢复。因此,在一般情况下,它不是无锁或无等待。

是否可以创建等待无锁或无锁的延迟初始化?

2 个答案:

答案 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散列到一个项目桶中,以尝试减少争用的频率。