linux系统编程里面的嵌套锁

在 Linux 系统编程中,嵌套锁(Nested Lock) 通常指允许同一线程多次获取同一个锁的机制(即 递归锁,Recursive Mutex)。这种设计用于解决函数调用链中需要重复加锁的场景(例如递归函数、多层函数嵌套访问共享资源),避免同一线程因重复加锁导致的死锁。以下是详细分析和实现指南:


1. 嵌套锁的核心概念

  • 递归加锁:同一线程可以多次对同一锁调用 lock,但必须调用相同次数的 unlock

  • 锁计数器:递归锁内部维护一个计数器,记录当前线程的加锁次数。

  • 应用场景

    • 递归函数访问共享资源。

    • 模块化代码中多个函数嵌套调用同一锁。


2. Linux 递归锁的实现(POSIX 线程库)

Linux 通过 pthread_mutex 的 递归属性 实现嵌套锁:

步骤 1:设置递归属性
#include <pthread.h>

int main() {
    pthread_mutex_t mutex;
    pthread_mutexattr_t attr;

    // 初始化属性对象
    pthread_mutexattr_init(&attr);
    
    // 设置互斥锁类型为递归锁
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    // 使用属性初始化互斥锁
    pthread_mutex_init(&mutex, &attr);

    // 销毁属性对象(属性已绑定到锁,后续不再需要)
    pthread_mutexattr_destroy(&attr);

    // ... 使用锁 ...

    pthread_mutex_destroy(&mutex);
    return 0;
}
 
步骤 2:递归加锁示例
void recursive_function(pthread_mutex_t *mutex, int depth) {
    pthread_mutex_lock(mutex);  // 第一次加锁
    if (depth > 0) {
        recursive_function(mutex, depth - 1);  // 递归调用,再次加锁
    }
    pthread_mutex_unlock(mutex);  // 每次解锁
}

int main() {
    pthread_mutex_t mutex;
    // 初始化递归锁(代码同上)
    
    recursive_function(&mutex, 3);  // 嵌套加锁3次,解锁3次
    
    pthread_mutex_destroy(&mutex);
    return 0;
}
 

3. 嵌套锁的注意事项

(1) 加锁与解锁必须严格配对
  • 每次 lock 必须对应一次 unlock,否则其他线程无法获取锁。

  • 错误示例

    pthread_mutex_lock(&mutex);  // 加锁1
    pthread_mutex_lock(&mutex);  // 加锁2(合法,因为这是递归锁)
    pthread_mutex_unlock(&mutex);  // 解锁1
    // 忘记第二次解锁!锁仍被持有,其他线程将阻塞。
     
(2) 避免跨线程解锁
  • 只有锁的持有者线程可以解锁,其他线程解锁会导致未定义行为。

    // 线程A
    pthread_mutex_lock(&mutex);
    // 线程B
    pthread_mutex_unlock(&mutex);  // 错误!EPERM 错误
     
(3) 性能开销
  • 递归锁比普通互斥锁略慢(需维护计数器),非必要场景避免滥用。


4. 嵌套锁的典型应用场景

场景 1:递归函数访问共享资源
// 递归遍历链表并修改节点
void traverse_list(Node *node, pthread_mutex_t *mutex) {
    if (node == NULL) return;
    
    pthread_mutex_lock(mutex);
    modify_node(node);  // 修改当前节点
    traverse_list(node->next, mutex);  // 递归调用(再次加锁)
    pthread_mutex_unlock(mutex);
}
 
场景 2:模块化代码中的锁传递
// 模块A的函数
void func_a(pthread_mutex_t *mutex) {
    pthread_mutex_lock(mutex);
    func_b(mutex);  // 调用模块B的函数(需要同一锁)
    pthread_mutex_unlock(mutex);
}

// 模块B的函数
void func_b(pthread_mutex_t *mutex) {
    pthread_mutex_lock(mutex);  // 再次加锁(合法)
    // 操作共享资源
    pthread_mutex_unlock(mutex);
}
 

6. 递归锁与普通锁的对比

特性递归锁普通锁(非递归)
同一线程重复加锁允许(计数器递增)导致死锁(如 PTHREAD_MUTEX_ERRORCHECK 返回 EDEADLK
性能略低(维护计数器)较高
适用场景嵌套函数/递归访问共享资源简单临界区

7. 综合示例:线程安全的递归目录遍历

#include <stdio.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

pthread_mutex_t mutex;

void list_dir(const char *path, int depth) {
    DIR *dir = opendir(path);
    if (!dir) return;

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        pthread_mutex_lock(&mutex);  // 加锁保护打印操作
        for (int i = 0; i < depth; i++) printf("  ");
        printf("%s\n", entry->d_name);
        pthread_mutex_unlock(&mutex);

        if (entry->d_type == DT_DIR) {
            char subpath[1024];
            snprintf(subpath, sizeof(subpath), "%s/%s", path, entry->d_name);
            list_dir(subpath, depth + 1);  // 递归调用,再次加锁
        }
    }
    closedir(dir);
}

int main() {
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&mutex, &attr);
    pthread_mutexattr_destroy(&attr);

    list_dir(".", 0);  // 从当前目录开始遍历

    pthread_mutex_destroy(&mutex);
    return 0;
}

                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式园姐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值