MST

星途 面试题库

面试题:C语言多线程中条件变量的死锁场景分析

在使用C语言结合Linux条件变量进行多线程编程时,可能会出现死锁情况。请举例说明一种可能导致死锁的场景,并阐述如何避免这种死锁?
30.4万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

可能导致死锁的场景示例

假设有两个线程 thread1thread2,它们共享两个资源 resource1resource2,并使用互斥锁 mutex1mutex2 来保护对这些资源的访问,同时使用条件变量 cond 进行线程间通信。

#include <pthread.h>
#include <stdio.h>

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int flag = 0;

void* thread1_func(void* arg) {
    pthread_mutex_lock(&mutex1);
    while (flag == 0) {
        pthread_cond_wait(&cond, &mutex1);
    }
    pthread_mutex_lock(&mutex2);
    // 访问共享资源 resource1 和 resource2
    printf("Thread 1 accessing resources\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

void* thread2_func(void* arg) {
    pthread_mutex_lock(&mutex2);
    flag = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_lock(&mutex1);
    // 访问共享资源 resource1 和 resource2
    printf("Thread 2 accessing resources\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}

在上述代码中,如果 thread1 先获取了 mutex1,然后进入 pthread_cond_wait 等待条件变量 cond,此时 mutex1 被解锁。接着 thread2 获取了 mutex2,设置 flag 为 1 并发送 cond 信号,但之后 thread2 尝试获取 mutex1,而 mutex1 此时被 thread1 持有(虽然 thread1pthread_cond_wait 中解锁了 mutex1,但还未重新获取),这样就会导致死锁。

避免死锁的方法

  1. 按顺序获取锁:所有线程按照相同的顺序获取互斥锁。例如,在上述场景中,无论是 thread1 还是 thread2,都先获取 mutex1,再获取 mutex2。修改后的 thread2_func 如下:
void* thread2_func(void* arg) {
    pthread_mutex_lock(&mutex1);
    flag = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_lock(&mutex2);
    // 访问共享资源 resource1 和 resource2
    printf("Thread 2 accessing resources\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}
  1. 使用锁层次:为每个锁分配一个层次编号,线程必须从低层次到高层次获取锁。这样可以确保所有线程以一致的顺序获取锁,避免死锁。
  2. 超时机制:在获取锁时设置超时,如果在一定时间内未能获取到锁,则放弃并释放已经获取的锁,然后重试或采取其他策略。例如,可以使用 pthread_mutex_timedlock 代替 pthread_mutex_lock
struct timespec timeout;
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += 1; // 设置超时时间为1秒
if (pthread_mutex_timedlock(&mutex1, &timeout) == ETIMEDOUT) {
    // 超时处理
    return NULL;
}
  1. 资源分配图算法:在复杂场景下,可以使用资源分配图算法(如死锁检测算法)定期检测死锁,并采取相应措施,如终止某些线程来打破死锁。