1.互斥锁
sleep waiting 获取锁失败 由内核调度进入休眠态。等待内核的通知,重新唤醒执行。由于进入了睡眠态,导致操作系统进行了线程的切换(有一定的开销 要保存线程的运行环境等)
特定:获取锁失败 会陷入睡眠 适用于互斥代码执行时间较长
1.1pthread_mutex_lock
pthread_mutex_lock(&mxlock);
++val;
pthread_mutex_unlock(&mxlock);
1.2pthread_mutex_trylock
好处就是不用阻塞 无论是自旋锁还是互斥锁 都立刻返回 用户可以在这期间执行其他操作
pthread_mutex_trylock
while(pthread_mutex_trylock(&mxlock) != 0){
printf("pthread_mutex_trylock fail thread:%d\n",pthread_self());
}
++val;
printf("i=:%d\n",val);
pthread_mutex_unlock(&mxlock);
2.自旋锁
busy waiting 通过CAS原子操作实现,加锁失败一直while 循环,直到获取锁成功。不会发生线程的切换。对于单核CPU,CPU必须是抢占式的(时间片用完切换)否则用自旋锁会导致这个CPU一直背占用
特定:一直占用CPU 适用于互斥代码执行时间较短,
pthread_spin_lock(&spinlock);
++val;
pthread_spin_unlock(&spinlock);
3.读写锁
保证读并发,写互斥。获取锁失败进入睡眠(互斥锁类似)
3.1 读优先:
A线程读获取到锁,B线程写获取锁失败则等待,C读 获取锁成功。 可能造成写线程饥饿,一直有读请求到来,导致写获取不到锁。
3.2写优先
A线程读获取到锁,B线程写获取锁失败则等待,C读 获取锁失败,A释放锁唤醒B线程(写锁)。 可能造成读饥饿
3.3公平锁
读写操作入队列,按顺序获取锁,保证了读并发 解决了饥饿问题
// 设置读优先
pthread_rwlockattr_setkind_np(&rwlock,PTHREAD_RWLOCK_PREFER_READER_NP);
pthread_rwlock_wrlock(&rwlock);
++val;
pthread_rwlock_unlock(&rwlock);
4.悲观锁
多线程下,共享资源容易发生冲突,在访问之前加锁。互斥锁 自旋锁 读写锁 都是属于悲观锁
5.乐观锁
多线程下,共享资源很少发生冲突,先修改资源,修改后进行验证是否发生冲突,冲突了则放弃操作、重试。乐观锁一般叫无锁编程。虽然乐观锁无锁,但是增加了重试的成本,只有冲突概率小 加锁成本高的场景才适用。
6.CAS
compare and swap, 通过CPU的硬件指令完成互斥资源的原子操作。
参考:https://zhuanlan.zhihu.com/p/246114725