
阿里云与数百位业务领导者和技术领导者携手,带您穿越移动中的新数字基础架构! >>>

在线程的实际运行过程中,我们经常需要多个线程来保持同步. 此时,您可以使用互斥锁来完成任务. 在使用互斥锁的过程中,主要有pthread_mutex_init,pthread_mutex_destory,pthread_mutex_lock,pthread_mutex_unlock几个函数来完成锁的初始化,锁的销毁,锁和释放锁的操作.
一个,创建锁
可以动态或静态创建锁. 您可以使用宏PTHREAD_MUTEX_INITIALIZER静态初始化锁. 这种方式更容易理解. 互斥锁是pthread_mutex_t的结构,而此宏是结构常数. 的初始化锁:
pthread_mutex_t互斥锁= PTHREAD_MUTEX_INITIALIZER;
可以使用pthread_mutex_init函数动态创建另一个锁. 函数原型如下:
int pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t * attr)
第二,锁的属性
可以通过pthread_mutexattr_init(pthread_mutexattr_t * mattr)初始化互斥锁属性;那么您可以调用其他属性设置方法来设置其属性;
互斥锁的范围: 您可以指定该进程是与其他进程同步还是在同一进程中的不同线程之间同步. 可以设置为
PTHREAD_PROCESS_SHARE和PTHREAD_PROCESS_PRIVATE
默认值是后者,这意味着在该过程中使用了锁.
您可以使用int pthread_mutexattr_setpshared(pthread_mutexattr_t * mattr,int pshared)
pthread_mutexattr_getshared(pthread_mutexattr_t * mattr,int * pshared)
设置和获取锁的范围;
互斥锁的类型具有以下值空间:
PTHREAD_MUTEX_TIMED_NP,这是默认值,这是普通锁定. 锁定线程后,其余请求锁定的线程将形成一个等待队列,并在解锁后根据优先级获得锁定. 这种锁定策略可确保资源分配的公平性.
PTHREAD_MUTEX_RECURSIVE_NP是一个嵌套锁,它允许同一线程多次成功获得同一锁,并通过多次解锁将其解锁. 如果是来自其他线程的请求,请在解锁锁定线程时重新竞争.
PTHREAD_MUTEX_ERRORCHECK_NP,错误检查锁,如果同一线程请求相同的锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP相同. 这确保了当不允许多个锁时,在最简单的情况下不会出现死锁.
PTHREAD_MUTEX_ADAPTIVE_NP,适用于锁(最简单的锁类型),仅等待解锁并重新竞争.
可用
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)

pthread_mutexattr_gettype(pthread_mutexattr_t * attr,int * type)
获取或设置锁的类型.
三,释放锁
原型:
int pthread_mutex_destroy(pthread_mutex_t * mp);
在调用pthread_mutex_destory之后,您可以释放该锁占用的资源,但是前提是当前未锁定该锁.
返回值:
pthread_mutex_destroy()成功完成后将返回零. 其他任何返回值均表示错误. 如果出现以下任何一种情况,该函数将失败并返回相应的值.
EINVAL: mp指定的值不引用初始化的互斥对象.
四次锁定操作
锁的操作主要包括锁定pthread_mutex_lock(),解锁pthread_mutex_unlock()和测试锁定pthread_mutex_trylock().
锁定互斥锁
原型:
int pthread_mutex_lock(pthread_mutex_t * mutex);
功能描述:
当pthread_mutex_lock()返回时,互斥体已被锁定. 调用线程是互斥锁的所有者. 如果该互斥锁已经被另一个线程锁定并由另一个线程拥有,则调用线程将阻塞,直到该互斥锁可用为止.
返回值:
pthread_mutex_lock()成功完成后将返回零. 其他任何返回值均表示错误. 如果出现以下任何一种情况,该函数将失败并返回相应的值.
EAGAIN: 由于已超过互斥锁的最大递归锁数,因此无法获取该互斥锁.
EDEADLK: 当前线程已经具有互斥锁.
解锁互斥锁
原型:
int pthread_mutex_unlock(pthread_mutex_t * mutex);

功能描述: pthread_mutex_unlock()可以释放互斥对象引用的互斥对象. 释放互斥锁的方法取决于互斥锁的type属性. 如果在调用pthread_mutex_unlock()时多个线程被某个互斥对象阻塞,则该互斥锁可用时的调度策略将确定获取该互斥锁的线程. 对于类型为PTHREAD_MUTEX_RECURSIVE的互斥锁,当计数达到零且调用线程不再锁定该互斥锁时,该互斥锁将变为可用.
返回值: pthread_mutex_unlock()成功完成后将返回零.
其他任何返回值均表示错误. 如果出现以下情况,该函数将失败并返回相应的值.
EPERM: 当前线程不拥有互斥锁.
使用无阻塞互斥锁
原型:
int pthread_mutex_trylock(pthread_mutex_t * mutex);
功能描述: pthread_mutex_trylock()是pthread_mutex_lock()的非阻塞版本. 如果互斥对象引用的互斥对象当前已被任何线程(包括当前线程)锁定,则该调用将立即返回. 否则,互斥锁将被锁定多线程 mutex,并且调用线程为其所有者.
返回值: pthread_mutex_trylock()成功完成后将返回零. 其他任何返回值均表示错误. 如果出现以下任何一种情况,该函数将失败并返回相应的值.
忙:
由于互斥锁指向的互斥锁已锁定,因此无法获取.
EAGAIN: 说明:
无法获取互斥锁,因为已经超过了互斥锁递归锁的最大数量.
五,使用锁
互斥锁用于确保一段时间内只有一个线程正在执行一段代码. 必要性显而易见: 假设每个线程将数据顺序写入同一文件,则最终结果必定是灾难性的.
首先让我们看下面的代码. 这是一个读/写程序,它们共享一个缓冲区,并且我们假定缓冲区只能保存一条信息. 也就是说,缓冲区只有两种状态: 有信息或无信息.
void reader_function(void);
void writer_function(void);
字符缓冲区;
int buffer_has_item = 0;
pthread_mutex_t互斥锁;
struct timespec延迟;
void main(void){

pthread_t阅读器;
/ *定义延迟时间* /
delay.tv_sec = 2;
delay.tv_nec = 0;
/ *使用默认属性初始化互斥对象* /
pthread_mutex_init(&mutex,NULL);
pthread_create(&reader,pthread_attr_default,(void *)&reader_function),NULL);
writer_function();
}
void writer_function(void){
while(1){
/ *锁定互斥锁* /
pthread_mutex_lock(&mutex);
如果(buffer_has_item == 0){
buffer = make_new_item();
buffer_has_item = 1;
}
/ *打开互斥锁* /
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
void reader_function(void){

while(1){
pthread_mutex_lock(&mutex);
if(buffer_has_item == 1){
consume_item(buffer);
buffer_has_item = 0;
}
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
程序说明:
互斥量变量互斥体在此处声明. 结构pthread_mutex_t是未公开的数据类型,其中包含系统分配的属性对象. 函数pthread_mutex_init用于生成互斥量. NULL参数指示使用默认属性. 如果需要声明特定的属性互斥锁,则必须调用函数pthread_mutexattr_init.
pthread_mutex_lock语句开始使用互斥锁进行锁定. 此后,代码被锁定,直到调用pthread_mutex_unlock,即一次只能由一个线程调用和执行. 当一个线程执行到pthread_mutex_lock时,如果此时另一个线程正在使用该锁,则该线程将被阻塞,即程序将等到另一个线程释放互斥量. 在上面的示例中,我们使用pthread_delay_np函数让线程休眠一段时间,只是为了防止线程一直占用该函数.
六个,死锁
需要提到的是,在使用互斥锁的过程中很可能会发生死锁: 两个线程试图同时占用两个资源,并以不同的顺序锁定相应的互斥锁,例如,两个线程需要锁定互斥锁1和互斥锁2,一个线程首先锁定互斥锁1,一个线程首先锁定互斥锁2,然后发生死锁. 有两种方法:
1. 我们可以使用函数pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本. 如果此时互斥锁尚未锁定,则pthread_mutex_trylock将返回0并锁定该互斥锁. 如果互斥锁已被锁定多线程 mutex,它将立即返回EBUSY,返回相应的信息,程序员可以对死锁进行相应的处理.
2. 调用pthread_mutexattr_init()创建互斥锁,然后调用pthread_mutexattr_settype设置属性.
1,快速输入. 此类型也是默认类型.
2. 递归类型. 如果遇到上述死锁情况,则同一线程周期性地锁定互斥锁,那么系统将知道锁定行为来自同一线程,然后它将同意该线程来锁定互斥锁.
3. 错误检测类型. 如果互斥锁已被锁定,则随后的锁定将失败而不会阻塞,并且pthread_mutex_lock()操作将返回EDEADLK.
七,总结
1. 在共享资源上进行操作之前,请确保已获取锁定.
2. 完成操作后,请确保释放锁定.
3. 尽可能短地占用锁.
4. 如果有多个锁,则获取顺序为ABC链扣,则释放顺序也应为ABC.
5. 当错误返回时,线程应释放其获取的锁.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-231828-1.html
更不能剥夺狗叫的权利