线程同步有关锁的术语介绍

在多线程中,锁是一种最常用的同步工具,下面详细讲讲带有锁字的一些术语:

1.锁的具体实现原理:

(1).互斥锁(Mutex)

用一个“互斥锁”的对象,任一时刻,只有一个线程能访问这个对象,也就是把代码分成一个个临界区域。在Linux下伪代码如下:

pthread_mutex_t mutex;

pthread_mutex_init (&mutex, NULL); /*初始化锁*/

pthread_mutex_lock(&mutex); /*获取互斥锁,也就是加锁*/

... /*临界区*/

pthread_mutex_unlock(&mutex); /*解锁互斥锁*/

如图,中间的临界区就实现了加锁,每次只有一个线程才能访问。基本上我们线程同步用的都是互斥锁。

2.自旋锁(Spin Lock)

跟互斥锁类型,都是为了实现互斥访问某个对象,但是互斥锁在资源被占用的时候会进入睡眠,而自旋锁则会一直循环去探测能否获取资源,在某种意义上,就是一直while循环探测。所以自旋锁很容易就占用cpu过多,但是不需要线程的休眠调度等,会效率比较高,适用加锁时间很短的情况。

3.读写锁(RWLock)

读写锁就是一个特殊的自旋锁,只是把需要进入临界区的访问者分成读者和写者,写者是排他的,但是允许多个读者同时存在。也就是说如果没有读者和写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者。如果读写锁没有写者,即使有其他读者,这个读者也可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。

互斥锁跟自旋锁一些优缺点:

Spinlock优点:没有昂贵的系统调用,一直处于用户态,执行速度快。

Spinlock缺点:一直占用cpu,而且在执行过程中还会锁bus总线,锁总线时其他处理器不能使用总线。

Mutex优点:不会忙等,得不到锁会sleep。

Mutex缺点:sleep时会陷入到内核态,需要昂贵的系统调用。

2.锁的一些分类

1.递归锁(Recursive Lock)和非递归锁(Non-Recursive Lock)

两个唯一的区别就是一个线程可以多次获取一个递归锁,而不会导致死锁,而多次获取一个非递归锁就会导致死锁。如下:

mutex_init(&mutex);
void fun()
{
  lock(&mutex);
  //do something1
  fun2();
  unlock&mutex);
}

void fun2()
{
  lock(&mutex);
  //do something2
  unlock(&mutex);
}

在fun()函数加锁调用了fun2(),而fun2()也加了锁,如果是非递归锁,那么fun获取了mutex,fun2再去获取mutex,就会导致死锁了。

2.乐观锁(Optimistic Lock)和悲观锁(Pessimistic Lock)

主要是用于关系型数据库存储。乐观锁,大多是基于数据版本(Version)记录机制实现,即为数据增加一个版本标识。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。以后提。优点是对于并发的数据库读取,避免了长时间的加锁开销等待,缺点也比较明显,可能导致不正确的数据存入数据库。悲观锁就比较容易理解了,也就是在整个数据库操作中,数据都处于锁定状态。也就是说在数据进行一个读的事务中,所有对整个数据的修改都没法进行。

3.死锁(Dead Lock)和活锁(Live Lock)

死锁大家应该都比较清楚,简单地说,就是进入死等待,例如P1占用了资源A,请求资源B,而P2占用了资源B,请求资源A,这样就导致双方一直在等待。而活锁就是资源一直轮不到自己使用,导致一直饥饿,例如如果事务T1封锁了数据R,事务T2请求封锁R,于是T2等待。T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。然后T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求,...,T2有可能永远等待,这就是活锁的情形。

主要是一些概念的介绍,具体的还需要具体去用了才能体会。

谢谢指教。

posted on 2013-10-21 01:41  VicStudio  阅读(1521)  评论(2编辑  收藏  举报