面试题答案
一键面试基于读写锁的并发控制方案设计
1. 数据结构定义
首先定义一个用于缓存数据的结构体,同时定义一个读写锁来控制对缓存数据的访问。
// 假设缓存数据类型为结构体
typedef struct {
// 具体的数据字段,例如:
char data[1024];
int size;
} CacheData;
// 定义全局缓存数据和读写锁
CacheData globalCache;
pthread_rwlock_t rwlock;
在C++ 中,可以这样实现:
#include <pthread.h>
#include <iostream>
#include <string>
struct CacheData {
std::string data;
int size;
};
CacheData globalCache;
pthread_rwlock_t rwlock;
2. 读操作实现
读操作使用读锁(共享锁),允许多个线程同时进行读操作,只要没有写操作正在进行。
void* readCache(void* arg) {
// 加读锁
pthread_rwlock_rdlock(&rwlock);
// 执行读操作
printf("Reading data: %s, size: %d\n", globalCache.data, globalCache.size);
// 释放读锁
pthread_rwlock_unlock(&rwlock);
return NULL;
}
C++ 实现:
void* readCache(void* arg) {
pthread_rwlock_rdlock(&rwlock);
std::cout << "Reading data: " << globalCache.data << ", size: " << globalCache.size << std::endl;
pthread_rwlock_unlock(&rwlock);
return NULL;
}
3. 写操作实现
写操作使用写锁(独占锁),确保在写操作进行时,没有其他线程可以进行读或写操作。
void* writeCache(void* arg) {
// 加写锁
pthread_rwlock_wrlock(&rwlock);
// 执行写操作
sprintf(globalCache.data, "new data");
globalCache.size = strlen(globalCache.data);
// 释放写锁
pthread_rwlock_unlock(&rwlock);
return NULL;
}
C++ 实现:
void* writeCache(void* arg) {
pthread_rwlock_wrlock(&rwlock);
globalCache.data = "new data";
globalCache.size = globalCache.data.size();
pthread_rwlock_unlock(&rwlock);
return NULL;
}
4. 初始化和清理
在程序开始时初始化读写锁,在程序结束时销毁读写锁。
int main() {
// 初始化读写锁
pthread_rwlock_init(&rwlock, NULL);
// 创建读线程和写线程
pthread_t readThreads[10], writeThread;
for (int i = 0; i < 10; i++) {
pthread_create(&readThreads[i], NULL, readCache, NULL);
}
pthread_create(&writeThread, NULL, writeCache, NULL);
// 等待线程结束
for (int i = 0; i < 10; i++) {
pthread_join(readThreads[i], NULL);
}
pthread_join(writeThread, NULL);
// 销毁读写锁
pthread_rwlock_destroy(&rwlock);
return 0;
}
C++ 实现:
int main() {
pthread_rwlock_init(&rwlock, NULL);
std::vector<pthread_t> readThreads(10);
pthread_t writeThread;
for (int i = 0; i < 10; i++) {
pthread_create(&readThreads[i], NULL, readCache, NULL);
}
pthread_create(&writeThread, NULL, writeCache, NULL);
for (int i = 0; i < 10; i++) {
pthread_join(readThreads[i], NULL);
}
pthread_join(writeThread, NULL);
pthread_rwlock_destroy(&rwlock);
return 0;
}
不同读写比例场景下的性能表现分析
1. 读多写少场景
- 性能优势:由于读操作使用读锁(共享锁),允许多个读线程同时访问缓存数据,在这种场景下,系统的并发读性能会很高。写操作虽然是独占锁,但由于写操作很少,对整体性能影响较小。例如,在一个文件系统缓存模块中,大量用户可能在同时读取文件的元数据(缓存中的数据),而只有文件更新时才会有写操作,这种情况下,读写锁能很好地满足需求,系统整体性能较高。
- 可能的问题:写操作可能会因为等待读锁释放而出现一定的延迟,不过因为写操作频率低,这种延迟对整体性能影响不大。
2. 写多读少场景
- 性能劣势:写操作需要获取独占锁,在写多读少场景下,频繁的写操作会导致读线程长时间等待写锁释放,从而降低读操作的并发性能。例如,在文件系统缓存模块中,如果频繁进行文件内容的修改(写操作),而很少有读取操作,那么读线程在等待写锁释放的过程中会造成资源浪费,系统整体性能会受到较大影响。
- 优化方向:可以考虑使用更细粒度的锁机制,比如针对不同的缓存区域使用不同的读写锁,减少锁的竞争范围。也可以采用写时复制(Copy - on - Write,COW)等技术,在写操作时先复制数据,减少对读操作的影响。
3. 读写均衡场景
- 性能表现:系统性能介于读多写少和写多读少之间。读写操作会交替竞争锁资源,读操作的并发优势和写操作的独占特性都会对性能产生影响。例如,在一个既频繁读取又频繁更新缓存数据的文件系统缓存模块中,可能会出现读线程和写线程都需要等待锁的情况,导致整体性能不如读多写少场景,但比写多读少场景要好一些。
- 优化措施:可以根据实际的读写比例动态调整锁的分配策略,例如,当读操作频率略有增加时,适当延长读锁的持有时间,反之亦然。