锁的引入是为了解决多线程处理共享资源的存取。Posix Thread提供一套mutex函数来处理互斥锁。互斥锁只有两种状态,上锁/解锁。当一个线程想要上锁一个已经上锁的互斥锁,则该线程就会挂起。A线程上锁了lock,B线程想要再次上锁lock。B线程挂起,直到A线程将lock解锁之后B线程才从挂起状态退出。
锁类型:
- 自旋锁:spinlock(不停判断锁是否可用,内核里面,效率高,占用cpu资源,用户态不提供)
- 互斥锁: mutex、信号量(节省CPU资源,效率不高,存在睡眠唤醒)
锁创建/销毁
创建锁
静态方式: 使用
PTHREAD_MUTEX_INITIALIZER
宏1
2pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
动态方式:
pthread_mutex_init()
函数定义:1
int pthread_mutex_init (pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
__mutexattr
用于指定互斥锁属性,NULL为缺省属性,通常使用NULL。
释放锁
1 | int pthread_mutex_destroy (pthread_mutex_t *__mutex) |
要求锁处于解锁状态,否则返回EBUSY
。在Linux中锁不占用任何资源。
锁操作
- 加锁
1 | int pthread_mutex_lock (pthread_mutex_t *__mutex); |
所有的锁都不可能被两个线程同时得到。
普通锁: 解锁者可以是同进程的任何线程。同一进程P,可以由A线程加锁一个普通锁,B线程解锁。
检错锁: 只有加锁者能解锁,否则返回EPERM。同一进程P,A线程加锁一个检错锁,B线程解锁该锁失败.
嵌套锁: 文档实现要求加锁者才能解锁,实验结果不一致?
解锁
1
int pthread_mutex_unlock (pthread_mutex_t *__mutex);
快速锁: 解除锁定。
递归锁:使锁上的引用计数-1。
检错锁: 解锁者和加锁者一致才解锁,否则啥也不做。测试锁
1 | int pthread_mutex_trylock (pthread_mutex_t *__mutex); |
和pthread_mutex_lock
语义相同,锁被占用时不会阻塞挂起,返回EBUSY
锁属性
属性:pthread_mutexattr_t mutexattr;
设置属性函数:pthread_mutexattr_settype(&mutexattr, kind);
类型:
PTHREAD_MUTEX_TIMED_NP
默认值,普通锁,快速锁。加锁之后,阻塞等待队列,解锁之后,阻塞队列中的先到的先得,有次序要求。普通锁容易死锁,线程A获得普通锁之后再次调用lock,A线程由于不能获得锁导致A挂起,进而导致死锁。
创建锁:1
2
3
4pthread_mutex_t mutex;
pthread_mutexattr_t mutexattr;
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_TIMED_NP);
pthread_mutex_init(&mutex, &mutexattr);PTHREAD_MUTEX_RECURSIVE_NP
,嵌套锁.同一个线程可获得同一个锁多次,通过多次unlock解锁,使用计数机制。不同线程请求,加锁线程解锁时重新竞争,怎么理解,解锁只有公平竞争,没有先后区别。
PTHREAD_MUTEX_ERRORCHECK_NP
,检错锁。同一个线程请求同一个锁,返回EDEADLK。否则和PTHREAD_MUTEX_TIMED_NP
动作一致。保证多次加锁不不会死锁。
线程在加锁后解锁前被取消,锁将永远处于锁定状态。需要在退出回调phtead_cleanup_push/pthread_cleanup_pop中解锁。
不应该在信号处理函数中使用互斥锁,容易造成死锁。
读写锁
为了并发读不受影响,使用读写锁进行共享变量的处理。
1 | int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t *attr); |
可以多个线程使用读锁,并不会冲突。但是当某个线程想要写锁时,如果其他线程已经获得过读锁,就会进入阻塞状态,等读锁全部释放之后才能上写锁。这时候就像互斥锁了。
1 | static char share_data[20] = {"HelloWorld"}; |
运行结果:
1 | I locked read lock [140487461066496] |
读是可以被并发读的,但是写的话必须等待所有的读锁释放了才能写。
读写锁并不是所有的操作系统都支持的。不是POSIX标准。