线程锁与信号量

线程锁,用于保护临界区,其类型主要有:

TODO: 本文将主要讲解各种锁的区别、原理、实现及应用,举例将使用C11标准进行举例,更多实现请看实现文件夹下对应的笔记。

互斥锁(mutex lock)

一个互斥锁只能同时加锁一次,加锁后需要主动解锁,若程序先后加锁,则后加锁的指令会被阻塞,直到该锁被其他线程解锁。

pthread

创建锁

对于pthread,其线程锁均有两种初始化方式,一种是构造函数初始化,一种是静态初始化。
静态初始化方法在其他库中较少给出。

构造函数初始化
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

加锁

阻塞加锁
pthread_mutex_lock(&mutex);
非阻塞加锁
pthread_mutex_try_lock(&mutex);

解锁

pthread_mutex_unlock(&mutex);

销毁

pthread_mutex_destroy(&mutex);

额外配置

这里仅包含一些基础的额外配置,实际上下列的recursive lockspin lock等均是使用额外配置进行使用的。

递归锁、可重入锁(recursive lock)

递归锁即在同一个线程内可以反复加锁的锁,但是每次加锁都需要解锁???

pthread

创建锁

// Init attribute.
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
// Set attribute.
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// Init lock.
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);

自旋锁(spin lock)

与互斥锁不同的是,互斥锁在等待解锁时,线程挂起,进入休眠状态,而自旋锁则会循环检测是否解锁,其主要用途是保护一小段短小的临界区代码,减小线程切换的时间开销。

与互斥锁相似的是,在同一个线程内多次加锁同样会造成死锁。

互斥锁主要用于:

  • 临界区有IO操作
  • 临界区代码复杂或循环量大
  • 临界区竞争激烈
  • 单核处理器

自旋锁主要用于:

  • 服务器等CPU资源充足且对时间有要求的场景

pthread

创建锁

构造函数初始化
pthread_spinlock_t spinlock;
pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED);
3```

##### 静态初始化
```C
pthread_spinlock_t spinlock = SPIN_LOCK_UNLOCKED;

读写锁(rw lock)

一种高级锁,该锁在读操作时是共享的,写操作时是排他的;即允许多个线程读取,但只允许一个线程写入,且其他线程在该锁被写入时无法读取。

信号量

是使用阻塞锁实现的一种