Double checked locking,两次检查的锁

最开始知道这个内容是在和室友聊天的时候发现的,它说国内某互联网公司中有一道笔试题,需要当场写出无锁队列的代码。当时对于这个概念还是一知半解,后来他说陈皓博客上有相关的内容,本着好学的精神就去一窥究竟:无锁队列的实现


基本思想是利用编译器或者语言提供的原子操作,CAS(compare and swap或者compare and set)。

bool compare-and-swap(Adderss addr, Value old, Value new)
{
    if (*addr = old) {
        *addr = new;
        return true;
    }
    return false;
}

其实原子操作是因为并行编程的问题而更加受到重视。因为在传统的单核CPU上,在同一时刻只有一个线程(或者进程)在运行。不同线程之间不会产生竞争,也就不就引入所谓的锁或者其他保证在临界区的互斥访问。


在多线程的环境下对于资源的分配需要给予足够的重视,一方面变量被重复初始化或者造成资源的丢失。在单例模式中:

class Singleton
{
public:
    static Singleton *instance (void)
    {
        if (instance_ == 0)
        // critical section
        instance_  = new Singleton;
        return instance_;
    }
   void method(void);
private:
    static Singleton *instance_;
};

在上面的critical section标记中,当多个线程同时到达这个地方时,instance_可能会被多次初始化。


简单直接的解决方法即枷锁

class Singleton
{
public:
	static Singleton *instacne(void)
	{
	  // Constructor of guard acquires
	  // lock_ automatically
	  Guard<Mutex> guard (lock_);
	  
	  // only one thread in the 
	  // critical section in a time
	  
	  if (instance_ == 0)
	    instance_ = new Singleton;
		
	  return instance_;
	 }
private:
	static Mutex lock_;
	static Singleton *instance_;
};

简单粗暴的加锁方式使得访问instance的效率大大降低,即使是访问instance_也需要获得锁。Schmidt等人提出一种Double checked locking方式,如下:

class Singleton
{
public:
	static Singleton *instacne(void)
	{
	  // first check
	  
	  if (instance_ == 0)
	  {
		Guard<Mutex> guard (lock_);
		if (instance_ == 0)
			instance_ = new Singleton;
	  // only one thread in the 
	  // critical section in a time	  
	 }
	 return instance_;
	}
private:
	static Mutex lock_;
	static Singleton *instance_;
};

这种双加锁的方式具有一定的应用场景,但是也不是万能的。看一段Java的利用double checked locking的代码:

public void addResult(String uri, ProfilerData data)
{
	Long key = new Long(data.getKey(uri));
	
	// critical section
	ProfilerResult result = (ProfilerResult)results.get(key);
	
	if (result == null)
	{
		synchronized(results)
		{
			if ((result == (ProfilerResult)results.get(key)) == null)
				results.put(key,result = new ProfilerResult(uri, RESULTS_COUNT));
		}
	}
	results.addData(data);
}

注意上述表明的critical section区域。这句话不是同步的,则后面的判断就会出错。所以经典的方法在使用时必须注意实际的应用性。关于并行程序最好是使用公开的算法,因为一般都受过检验。


本文完


参考:

[1] Schmidt等 double checked locking

[2] Code quality

[3] C++ concurrency in action, Jolt奖的书




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值