mutex lock,spinlock,rwlock和condlock的理解

Mutex Lock

概述

互斥锁,一条线程加锁锁住临界区,另一条线程尝试访问改临界区的时候,会发生阻塞,并进入休眠状态。

具体说明

假设一台机器上的cpu有两个核心core0和core1,现在有线程A、B、C,此时core0运行线程A,core1运行线程B,此时线程B使用Mutex锁,锁住一个临界区,当线程A试图访问该临界区时,因为线程B已经将其锁住,因此线程A被挂起,进入休眠状态,此时core0进行上下文切换,将线程A放入休眠队列中,然后core0运行线程C,当线程B完成临界区的流程并执行解锁之后,线程A又会被唤醒,core0重新运行线程A

Spin Lock

概述

自旋锁,一条线程加锁锁住临界区,另一条线程尝试访问该临界区的时候,会发生阻塞,但是不会进入休眠状态,并且不断轮询该锁,直至原来锁住临界区的线程解锁。

具体说明

假设一台机器上有两个核心core0和core1,现在有线程A、B、C,此时core0运行线程A,core1运行线程B,此时线程B调用spin lock锁住临界区,当线程A尝试访问该临界区时,因为B已经加锁,此时线程A会阻塞,并且不断轮询该锁,不会交出core0的使用权,当线程B释放锁时,A开始执行临界区逻辑

需要注意的地方

  • spin lock临界区逻辑,不应该出现io或者是密集运算,因为spinlock不释放core的使用权,因此不断轮询如果临界区代码过于复杂,那么会降低机器性能,因此临界区代码应该足够简单
  • spin lock临界区逻辑,不能出现主动中断的逻辑,比如调用sleep,而要保证spinlock的原子性,因为,如果临界区逻辑被挂起,另一个线程执行访问该临界区时,因为之前的锁没有被释放,并且spinlock不会交出core的使用权,因此会死等之前加锁的线程解锁,因为之前加锁的线程已经被挂起,因此此时会出现死锁状态
  • 不应该递归逻辑中使用spinlock,因为第一次执行加锁,锁住临界区,递归后尝试访问临界区,此时该线程会死等第一次调用释放锁,而递归调用又依赖某个层级的访问结束返回才能执行完毕,而进入第二层级的访问时,就已经不能执行下去了,因此陷入死锁

Read Write Lock

概述

读写锁,一共三种状态

  • 读状态时加锁,此时为共享锁,当一个线程加了读锁时,其他线程如果也尝试以读模式进入临界区,那么不会发生阻塞,直接访问临界区
  • 写状态时加锁,此时为独占锁,当某个线程加了写锁,那么其他线程尝试访问该临界区(不论是读还是写),都会阻塞等待
  • 不加锁

注意

  • 某线程加读取锁时,允许其他线程以读模式进入,此时如果有一个线程尝试以写模式访问临界区时,该线程会被阻塞,而其后尝试以读方式访问该临界区的线程也会被阻塞
  • 读写锁适合在读远大于写的情形中使用

Cond Lock

概述

一种通过信号驱动的锁,一般来说,这种模式一般需要有一个发送信号的线程,及若干个被动接收信号的线程,条件锁一般要和互斥锁结合使用

Pthread中的条件锁

  • pthread_cond_init初始化
  • pthread_cond_signal只会触发一个正在wait的线程,优先级最高的线程最先被唤醒,如果优先级都一样,则优先唤醒睡眠最长的那个线程
  • pthread_cond_broadcast唤醒所有线程
  • pthread_cond_wait阻塞一个线程,并等待信号唤醒它,在wait的开始逻辑,会写入优先级信息,而这个优先级信息是共享的,因此需要在调用phtread_cond_wait之前,调用pthread_mutex_lock,phtread_cond_wait函数会在优先级信息写完后,调用一次pthread_cond_unlock进行解锁,然后进入block状态,当被signal唤醒时,它又会再加一次锁,经典用法
Thread1
pthread_cond_signal(g_cond)

Thread2
pthread_mutex_lock(&g_mutex)
while(condition)
    pthread_cond_wait(&g_cond, &g_mutex)
pthread_mutex_unlock(&g_mutex)
lock