示例代码如下:
class ResourcePool{
public:
...
static inline ResourcePool* singleton()
{
ResourcePool* p = _singleton.load(butil::memory_order_consume);
if (p) {
return p;
}
pthread_mutex_lock(&_singleton_mutex);
p = _singleton.load(butil::memory_order_consume);
if (!p) {
p = new ResourcePool();
_singleton.store(p, butil::memory_order_release);
}
pthread_mutex_unlock(&_singleton_mutex);
return p;
}
private:
....
static butil::static_atomic<ResourcePool*> _singleton;
static pthread_mutex_t _singleton_mutex;
};
Butil:: memoryreorder_consume
是memoryreorder_consume的封装的boost内存顺序
第一个问题
在多核多线程环境下,第一个“butil::memoryreorder_consume”保证了指令的顺序,并防止其他线程在获取p指针之前解引用p?
第二个问题:
如果ResourcePool已经存在,则返回
p=_singleton. load (but il:: memoryreorder_consume) between them
如果后面没有创建,则创建。后续的 _singleton. store (p, but il:: memoryreorder-release)
应该已经通知给其他线程了,只要其他线程 _singleton. load (p, but il:: memoryreorder-acquire)
这不是无锁实现,为什么要加锁?
如果需要加锁才能保证
p=_singleton. load (butil:: memoryreorder_consume)
唯一访问,那么后面的 _singleton. store (p, but il:: memoryreorder-release)
有什么用呢?
您得到的是一个双重检查锁定的示例,众所周知,正确执行该操作非常困难。双重检查锁定的目标是确保在分配
_singleton_mutex
成员后,没有线程必须锁定 _singleton
。
互斥量的目的是防止多个线程构造
new ResourcePool()
。这是一个复杂的操作,除了使用互斥体之外,没有其他方法可以将构造函数调用和 _singleton
赋值转换为单个原子操作。
如果您不介意制作 singleton
函数的 乐观版本,那么您将
不需要需要互斥体。乐观的版本可能允许多个线程构造一个新的
ResourcePool
,但它将保证所有调用者都会同意哪一个是真实的,并且它将保证所有额外的 ResourcePool
对象将被删除而不使用。
原子变量的目的当然是提供一种无锁的方式,以确保一旦
_singleton
被赋值,任何线程都不会误认为它没有被赋值。