面试题答案
一键面试优化策略
- 减少锁粒度:
- 策略:将大的共享资源分解为多个小的独立部分,每个部分使用单独的互斥锁。这样不同线程可以同时访问不同部分的资源,减少锁竞争。
- 举例:假设有一个包含用户信息(姓名、年龄、地址)的结构体作为共享资源。可以将其拆分为三个结构体,分别管理姓名、年龄和地址,每个结构体使用一个互斥锁。例如:
std::mutex nameMutex;
std::mutex ageMutex;
std::mutex addressMutex;
std::string getName() {
std::lock_guard<std::mutex> lock(nameMutex);
// 返回姓名相关操作
}
int getAge() {
std::lock_guard<std::mutex> lock(ageMutex);
// 返回年龄相关操作
}
std::string getAddress() {
std::lock_guard<std::mutex> lock(addressMutex);
// 返回地址相关操作
}
- 读写锁:
- 策略:如果共享资源读操作远多于写操作,可以使用读写锁(如
std::shared_mutex
)。多个线程可以同时进行读操作,只有写操作需要独占锁,从而提高并发性能。 - 举例:
- 策略:如果共享资源读操作远多于写操作,可以使用读写锁(如
std::shared_mutex sharedDataMutex;
std::vector<int> sharedData;
void readData() {
std::shared_lock<std::shared_mutex> lock(sharedDataMutex);
// 读取sharedData的操作
}
void writeData(int value) {
std::unique_lock<std::shared_mutex> lock(sharedDataMutex);
sharedData.push_back(value);
}
- 无锁数据结构:
- 策略:采用无锁数据结构,如无锁队列(
std::atomic<>
相关实现),这些数据结构通过原子操作实现线程安全,避免了传统锁的竞争开销。 - 举例:使用
std::atomic<int>
实现一个简单的计数器:
- 策略:采用无锁数据结构,如无锁队列(
std::atomic<int> counter(0);
void incrementCounter() {
counter++;
}
int getCounterValue() {
return counter.load();
}
结合条件变量和互斥锁减少不必要的锁竞争
- 原理:条件变量用于线程间的同步,当某个条件满足时通知等待的线程。结合互斥锁,线程在等待条件变量时可以释放互斥锁,避免一直持有锁导致其他线程无法访问共享资源,从而减少锁竞争。
- 示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> dataQueue;
bool ready = false;
void producer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
dataQueue.push(i);
std::cout << "Produced: " << i << std::endl;
ready = true;
lock.unlock();
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready ||!dataQueue.empty(); });
if (dataQueue.empty()) break;
int value = dataQueue.front();
dataQueue.pop();
std::cout << "Consumed: " << value << std::endl;
if (dataQueue.empty()) ready = false;
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
在上述代码中,producer
线程生产数据并通知consumer
线程,consumer
线程在条件变量cv
上等待,等待时释放互斥锁mtx
,只有当条件满足(ready
为true
或队列不为空)时才重新获取锁并处理数据,从而减少了不必要的锁竞争。