1. 简述
在Linux系统中,pthread_mutex_t 是由 libpthread 线程库提供的互斥锁机制,它用于同步多个线程对共享资源的访问,以防止竞态条件和数据不一致的问题。pthread_mutex_t 提供了多种类型的互斥锁,包括普通的互斥锁、递归互斥锁、读写锁等,以适应不同的同步需求。
pthread_mutex_t 互斥锁的基本原理是通过原子操作来控制对共享资源的访问。当一个线程尝试获取已经被其他线程持有的互斥锁时,该线程会被阻塞,直到互斥锁被释放。互斥锁内部通常使用原子变量或自旋锁等机制来确保操作的原子性和线程间的协调。
2. 特点
线程安全:pthread_mutex_t 保证了在多线程环境下对共享资源的访问是线程安全的。
可重入性:递归互斥锁允许同一个线程多次获取同一个锁,每次成功获取锁都会增加一个计数,释放锁时必须释放与获取次数相同的次数。
公平性:可以选择公平性的互斥锁,确保线程按照请求锁的顺序来获取锁。
阻塞和非阻塞:提供了阻塞式的锁获取操作(如 pthread_mutex_lock),也有非阻塞式的操作(如 pthread_mutex_trylock)。
3. Linux互斥锁的使用
(1)初始化:
使用pthread_mutex_init对互斥锁进行初始化,第二个参数表示使用默认属性。
或直接静态初始化:
(2)初始化和销毁锁属性
(3)设置互斥锁属性
此函数用于在属性对象 attr 中设置互斥锁的类型 kind,kind 可以是如下几种:
PTHREAD_MUTEX_NORMAL:标准互斥锁。
PTHREAD_MUTEX_RECURSIVE:递归互斥锁。
PTHREAD_MUTEX_ERRORCHECK:错误检查互斥锁。
PTHREAD_MUTEX_ADAPTIVE_NP:自适应非优先级互斥锁(非优先级)。
PTHREAD_MUTEX_DEFAULT_NP:默认互斥锁(非优先级)。
(4)获取互斥锁属性
此函数用于从属性对象 attr 中获取当前设置的互斥锁类型,并将其存储在 kind 指向的位置。
(5)设置和获取协议属性
此函数用于在属性对象 attr 中设置和获取互斥锁的优先级协议 protocol。
(6)加锁
当我们在尝试对一段共享资源进行加锁时,如果该共享资源已经被其他线程锁定,那么当前线程将在此处阻塞,知道其他线程释放该锁。
(7)尝试加锁
有的时候,我们并不希望如上述阻塞住,这个时候我们可以使用尝试加锁API,这样如果该锁已经被其他线程获得(上锁),当前函数将立即返回。这样有利于程序充分利用CPU资源,先去处理其他事物。
(8)解锁
执行此API将释放互斥锁,以便于其他线程获得该所,处理相关事务。
(9)销毁互斥锁
当所使用完毕后,调用此API销毁互斥锁。
4. 读写锁
除了普通的互斥锁,libpthread 还提供了读写锁(pthread_rwlock_t),它允许多个线程同时读取共享资源,但同一时间只允许一个线程写入。这种锁在读多写少的场景中非常有用,因为它允许并发的读取操作,提高了性能。
使用读写锁的方法与互斥锁类似,包括初始化、加锁、尝试加锁、解锁和销毁等操作,但函数名称不同。
(1)读写锁初始化
(2)初始化和销毁属性变量
(3)获取和设置初始化属性
(4)加锁(阻塞式)
(5)尝试加锁(非阻塞式)
(6)写加锁
(6)销毁锁
5. 死锁
死锁(Deadlock)一般发生在多个进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的状态,若无外力作用,这些进程都无法继续执行。死锁的产生的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用。
请求和保持条件:一个进程因请求其他资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
循环等待条件:存在一组进程,其中每个进程都在等待下一个进程所持有的资源。
死锁的产生情况
当两个或多个进程互相等待对方释放资源时。
当进程在等待资源的同时,还持有其他进程所需的资源。
当系统资源不足,无法满足所有进程的需求时。
死锁的处理方法
预防:破坏死锁的四个必要条件中的一个或多个。例如,可以通过资源一次性分配法来破坏请求和保持条件,或者通过资源有序分配法来破坏循环等待条件。
避免:在资源分配前进行安全性检查,确保分配后不会导致死锁。可以使用银行家算法来进行安全性检查。
检测:允许死锁发生,但通过死锁检测机制来识别死锁,并采取措施解决。可以使用资源分配图(Resource Allocation Graph)来检测循环等待。
解除:当检测到死锁后,采取措施来打破死锁。这可能涉及到终止进程或回滚操作,以释放资源。
银行家算法