您的位置:首页 > 文旅 > 旅游 > linux c 递归锁的介绍

linux c 递归锁的介绍

2024/10/6 18:28:51 来源:https://blog.csdn.net/zhu_superman/article/details/140622035  浏览:    关键词:linux c 递归锁的介绍

递归锁的递归特性确实只是对于持有锁的线程。当一个线程获取了递归锁后,它可以多次重复获取该锁,而不会导致自身阻塞或死锁。这是递归锁的重要特点,它允许同一个线程在已经持有锁的情况下,再次获取相同的锁。

然而,对于其他线程,如果该递归锁已经被某个线程持有(即使是持有锁的线程自身多次获取的),那么其他线程在尝试获取该锁时,仍然会被阻塞,直到持有锁的线程进行了相应次数的解锁操作,使得锁完全释放后,其他线程才有机会获取该锁。

递归锁通常应用于递归函数或方法,以及存在嵌套临界区的情况。在这些场景下,同一个线程可能需要在不同的递归层次或嵌套的临界区中多次获取同一个锁。通过使用递归锁,可以确保线程不会因为自身重复获取锁而产生死锁问题。

需要注意的是,使用递归锁时,持有锁的线程必须进行相应次数的解锁操作,以平衡之前的加锁操作。也就是说,对于每次加锁,都需要有一个对应的解锁,只有当解锁次数与加锁次数相等时,锁才会完全释放,其他线程才能获取该锁。如果解锁次数不足或过多,都可能导致死锁或其他同步问题的发生。

如在一个递归函数中,每次递归调用都需要获取相同的锁进行保护,递归锁就可以满足这种需求。而如果使用普通的互斥锁,在同一线程中再次尝试获取已经持有的锁时,就会导致死锁。

PTHREAD_MUTEX_RECURSIVE 是一种递归锁,它允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。

使用 PTHREAD_MUTEX_RECURSIVE 递归锁的一般步骤如下:

  1. 包含必要的头文件:#include <pthread.h>
  2. 初始化互斥锁属性对象:使用 pthread_mutexattr_init(&attr); 初始化一个互斥锁属性对象 attr
  3. 设置锁的类型为递归锁:通过 pthread_mutexattr_settype(&attr, pthread_mutex_recursive); 将互斥锁的类型设置为递归锁。
  4. 初始化递归锁:使用 pthread_mutex_init(&mutex, &attr); 初始化递归锁 mutex,并关联之前设置好属性的 attr 对象。
  5. 在需要加锁的代码段前加锁:通过 pthread_mutex_lock(&mutex); 进行加锁操作。如果是在同一个线程中且之前已经加过锁,不会产生死锁,而是可以成功再次加锁。
  6. 在相应的代码段结束后解锁:调用 pthread_mutex_unlock(&mutex); 释放锁。需要注意的是,加锁几次就需要解锁几次,才能完全释放该锁,以便其他线程获取该锁。
  7. 不再使用递归锁后,销毁互斥锁:使用 pthread_mutex_destroy(&mutex); 释放锁资源。

下面是一个简单的示例代码,演示了递归锁的使用:

#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>pthread_mutex_t mutex;
pthread_mutexattr_t attr;void*func(void*arg){if(*(char*)arg=='\0') return NULL;pthread_mutex_lock(&mutex);  // 加锁char*str = (char*)arg;while(*str!='\0'){fputc(*str, stdout);str++;}fputc('\n', stdout);func((char*)arg + 1);  // 递归调用自身,再次加锁pthread_mutex_unlock(&mutex);  // 解锁return NULL;
}int main()
{int ret;if((ret=pthread_mutexattr_init(&attr))!=0){  // 初始化互斥锁属性对象fprintf(stderr, "create mutex attribute error.msg:%s", strerror(ret));exit(1);}pthread_mutexattr_settype(&attr, pthread_mutex_recursive);  // 设置为递归锁属性pthread_mutex_init(&mutex, &attr);  // 初始化递归锁pthread_t p1, p2;char str1[8], str2[8];sprintf(str1, "abcdefg");sprintf(str2, "1234567");if((ret=pthread_create(&p1, NULL, func, str1))!=0){  // 创建线程 p1 并执行 func 函数fprintf(stderr, "create thread error.msg:%s", strerror(ret));exit(1);}if((ret=pthread_create(&p2, NULL, func, str2))!=0){  // 创建线程 p2 并执行 func 函数fprintf(stderr, "create thread error.msg:%s", strerror(ret));exit(1);}pthread_join(p1, NULL);  // 等待线程 p1 结束pthread_join(p2, NULL);  // 等待线程 p2 结束
}

在上述示例中,func 函数中存在递归调用,并且在递归调用时会再次对同一个锁进行加锁操作。如果使用普通的互斥锁(非递归锁),则会导致死锁。而使用 PTHREAD_MUTEX_RECURSIVE 递归锁,就可以在同一个线程中多次加锁而不会产生死锁。

需要注意的是,虽然递归锁提供了方便,但也应该谨慎使用,尽量避免在不必要的情况下过度使用递归锁,因为它可能会导致一些难以察觉的逻辑错误或性能问题。在实际编程中,确保正确地管理锁的获取和释放次数,以避免出现意外的情况。另外,在使用完递归锁后,记得使用 pthread_mutex_destroy 函数销毁锁,以释放相关资源。

在POSIX标准中,递归互斥锁的类型通常被定义为PTHREAD_MUTEX_RECURSIVE。但是在一些GNU系统(如Linux),PTHREAD_MUTEX_RECURSIVE常被定义为PTHREAD_MUTEX_RECURSIVE_NP,其中NP代表“Non-Portable”(非可移植的)。

具体来说:

  • PTHREAD_MUTEX_RECURSIVE:POSIX标准定义的递归互斥锁类型。这在POSIX兼容的系统中应该可以直接使用。
  • PTHREAD_MUTEX_RECURSIVE_NP:GNU特定的递归互斥锁类型。在一些GNU系统中,这个宏定义被用来替代标准的PTHREAD_MUTEX_RECURSIVE

通常,POSIX兼容系统(如现代的Linux发行版)应该支持PTHREAD_MUTEX_RECURSIVE。但是,如果你的系统使用了较旧的或特定的GNU库版本,你可能需要使用PTHREAD_MUTEX_RECURSIVE_NP

检查和使用合适的宏

为了编写兼容性更好的代码,可以在编译时检查是否定义了PTHREAD_MUTEX_RECURSIVE,并根据需要使用适当的宏。例如:

#include <pthread.h>int main() {pthread_mutexattr_t attr;pthread_mutexattr_init(&attr);#ifdef PTHREAD_MUTEX_RECURSIVEpthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#elsepthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
#endif// 其余代码...pthread_mutexattr_destroy(&attr);return 0;
}

定义_GNU_SOURCE

在包含<pthread.h>之前定义_GNU_SOURCE宏也可以启用所有GNU扩展功能,确保PTHREAD_MUTEX_RECURSIVE宏被正确识别:

#define _GNU_SOURCE
#include <pthread.h>// 其余代码...

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com