面试题答案
一键面试优化方面
- 锁的粒度:
- 分析:尽量减小锁的粒度,只对关键的共享资源进行加锁。如果锁的粒度太大,会导致过多的线程等待,降低并行性。
- 示例:假设程序中有一个大的数据结构,包含多个独立的子部分。如果每次操作都对整个数据结构加锁,那么不同线程对不同子部分的操作也会相互等待。优化时,可以为每个子部分设置单独的锁。
- 获取和释放锁的时机:
- 分析:在需要访问共享资源前尽早获取锁,访问结束后尽快释放锁,减少锁的持有时间。
- 示例:避免在持有锁的情况下执行长时间运行的任务,比如进行大量的计算或者I/O操作。
- 死锁避免:
- 分析:多层互斥锁嵌套容易导致死锁,要确保按照相同的顺序获取锁,避免形成死锁环。
- 示例:规定所有线程获取锁的顺序,例如总是先获取锁A,再获取锁B,以此类推。
代码示例
#include <pthread.h>
#include <stdio.h>
// 定义两个互斥锁
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
// 共享资源
int shared_variable1 = 0;
int shared_variable2 = 0;
// 线程函数1
void* thread_function1(void* arg) {
// 优化前,锁粒度大
// pthread_mutex_lock(&mutex1);
// shared_variable1++;
// shared_variable2++;
// pthread_mutex_unlock(&mutex1);
// 优化后,减小锁粒度
pthread_mutex_lock(&mutex1);
shared_variable1++;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock(&mutex2);
shared_variable2++;
pthread_mutex_unlock(&mutex2);
return NULL;
}
// 线程函数2
void* thread_function2(void* arg) {
// 优化前,锁粒度大
// pthread_mutex_lock(&mutex1);
// shared_variable1--;
// shared_variable2--;
// pthread_mutex_unlock(&mutex1);
// 优化后,减小锁粒度
pthread_mutex_lock(&mutex1);
shared_variable1--;
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock(&mutex2);
shared_variable2--;
pthread_mutex_unlock(&mutex2);
return NULL;
}
int main() {
pthread_t thread1, thread2;
// 创建线程
pthread_create(&thread1, NULL, thread_function1, NULL);
pthread_create(&thread2, NULL, thread_function2, NULL);
// 等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
// 销毁互斥锁
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}
在上述代码中,优化前对两个共享变量的操作都使用同一个锁,锁粒度较大。优化后为每个共享变量设置单独的锁,减小了锁粒度,提升了并行性,在不影响程序正确性的前提下提升了性能。同时,两个线程获取锁的顺序一致,避免了死锁的发生。在实际应用中,还需要根据具体的业务逻辑和共享资源的访问模式进一步调整锁的获取和释放时机,以达到更好的性能优化效果。