面试题答案
一键面试可能导致死锁的场景示例
假设有两个线程 thread1
和 thread2
,它们共享两个资源 resource1
和 resource2
,并使用互斥锁 mutex1
和 mutex2
来保护对这些资源的访问,同时使用条件变量 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
持有(虽然 thread1
在 pthread_cond_wait
中解锁了 mutex1
,但还未重新获取),这样就会导致死锁。
避免死锁的方法
- 按顺序获取锁:所有线程按照相同的顺序获取互斥锁。例如,在上述场景中,无论是
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;
}
- 使用锁层次:为每个锁分配一个层次编号,线程必须从低层次到高层次获取锁。这样可以确保所有线程以一致的顺序获取锁,避免死锁。
- 超时机制:在获取锁时设置超时,如果在一定时间内未能获取到锁,则放弃并释放已经获取的锁,然后重试或采取其他策略。例如,可以使用
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;
}
- 资源分配图算法:在复杂场景下,可以使用资源分配图算法(如死锁检测算法)定期检测死锁,并采取相应措施,如终止某些线程来打破死锁。