MST

星途 面试题库

面试题:C语言多线程性能优化与死锁检测

在一个复杂的Linux C语言多线程应用程序中,包含多个线程和多种类型的共享资源。假设程序出现性能瓶颈和潜在死锁问题。请描述如何使用工具(如valgrind等)来检测死锁,以及从代码层面(如优化锁的粒度、锁的获取顺序等)如何对多线程性能进行优化。同时,给出一个可能导致死锁的代码示例,并说明如何修改代码以避免死锁。
20.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

使用工具检测死锁

  1. Valgrind
    • 安装:在大多数Linux发行版上,可以通过包管理器安装Valgrind,如在Ubuntu上使用sudo apt - get install valgrind
    • 使用:运行程序时带上valgrind --tool = helgrind参数,例如valgrind --tool = helgrind./your_program。Helgrind是Valgrind的一个工具,专门用于检测多线程程序中的竞争条件和死锁。它会分析程序的线程交互,并报告潜在的问题。

代码层面优化多线程性能

  1. 优化锁的粒度
    • 含义:锁的粒度指的是锁所保护的共享资源的范围。
    • 优化方式:尽量减小锁的粒度,只对真正需要保护的共享资源加锁。例如,如果有一个大型的数据结构,而不同的线程只访问其中的不同部分,可以为每个部分分别设置锁,而不是对整个数据结构加一把大锁。这样可以提高并发度,减少线程等待锁的时间。
  2. 优化锁的获取顺序
    • 含义:多个线程获取多个锁时,按照一致的顺序获取锁可以避免死锁。
    • 优化方式:为所有需要获取的锁分配一个全局的唯一编号,所有线程都按照编号从小到大的顺序获取锁。例如,有锁A、锁B、锁C,编号分别为1、2、3,那么所有线程都先获取锁A,再获取锁B,最后获取锁C。

可能导致死锁的代码示例

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

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void *thread1(void *arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 1 locked mutex1\n");
    pthread_mutex_lock(&mutex2);
    printf("Thread 1 locked mutex2\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

void *thread2(void *arg) {
    pthread_mutex_lock(&mutex2);
    printf("Thread 2 locked mutex2\n");
    pthread_mutex_lock(&mutex1);
    printf("Thread 2 locked mutex1\n");
    pthread_mutex_unlock(&mutex1);
    pthread_mutex_unlock(&mutex2);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread1, NULL);
    pthread_create(&tid2, NULL, thread2, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    return 0;
}

在这个示例中,thread1先获取mutex1再获取mutex2,而thread2先获取mutex2再获取mutex1,如果thread1获取了mutex1thread2获取了mutex2,就会发生死锁。

修改代码以避免死锁

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

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void *thread1(void *arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 1 locked mutex1\n");
    pthread_mutex_lock(&mutex2);
    printf("Thread 1 locked mutex2\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

void *thread2(void *arg) {
    pthread_mutex_lock(&mutex1);
    printf("Thread 2 locked mutex1\n");
    pthread_mutex_lock(&mutex2);
    printf("Thread 2 locked mutex2\n");
    pthread_mutex_unlock(&mutex2);
    pthread_mutex_unlock(&mutex1);
    return NULL;
}

int main() {
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread1, NULL);
    pthread_create(&tid2, NULL, thread2, NULL);
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_mutex_destroy(&mutex1);
    pthread_mutex_destroy(&mutex2);
    return 0;
}

修改后的代码,thread1thread2都按照先获取mutex1再获取mutex2的顺序加锁,避免了死锁的发生。