2014-02-08 75 views
1

我一直在寻找懒惰初始化(让我们说全局变量,但它可能是任何东西)。到目前为止,我想出了是一样的东西等待免费或无锁初始化

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

此代码甚至不无锁...... –

+0

这显然不是无锁。这是问题的根源。如果我有无锁的惰性初始化,为什么我会问一个问题,询问它是否可能? – Puppy

+0

够公平的;我被复杂的代码抛弃了 - 如果你只是想要一个锁定的懒惰初始化,你可以使用一个'static'变量并完成它。 –

回答

3

如果你愿意来初始化多个对象,那么你可以通过只存储一个指针使lockfree代码:

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; 
} 

的lockfree算法的代价是你一般要“尝试和失败“,所以你必须支付本地尝试的代价,即使你必须丢弃结果。

正如您正确地指出的那样,如果只有一个线程执行初始化,那么您总是依赖于该单个线程,并且不能无锁定。


您将需要相应的清理代码也一样,如果析构函数有副作用:

delete p.exchange(nullptr); 
+0

是我刚刚想出了。严格地说,ISN的方法这个算法是否也是免等待的? – Puppy

+0

@DeadMG:如果你想成为无锁的,你永远不能依赖任何单一的线程。无锁的全部目的是即使任意一个任意线程数量无限期停止。 –

+0

@DeadMG:很可能,因为它没有循环... –

0

我不会认为任何一种情况都是可能的。如果你是作为全局初始化的一部分,在调用main之前,创建线程之前,你就有机会了。它如何可以等待或无锁定?

如果您允许初始化失败并稍后重试,它可能是无锁的。 如果另一个线程有该项目,它不能等待。

如果你关心这里的性能,那么你可以使用线程ID来散列成一堆物品,以尝试减少争用的频率。