MST

星途 面试题库

面试题:C++多线程中互斥锁的性能优化场景

在一个高并发的C++多线程程序中,存在大量线程频繁竞争互斥锁访问共享资源,导致性能瓶颈。阐述你可能采取哪些策略来优化性能,并举例说明如何结合条件变量和互斥锁来减少不必要的锁竞争。
10.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

优化策略

  1. 减少锁粒度
    • 策略:将大的共享资源分解为多个小的独立部分,每个部分使用单独的互斥锁。这样不同线程可以同时访问不同部分的资源,减少锁竞争。
    • 举例:假设有一个包含用户信息(姓名、年龄、地址)的结构体作为共享资源。可以将其拆分为三个结构体,分别管理姓名、年龄和地址,每个结构体使用一个互斥锁。例如:
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);
    // 返回地址相关操作
}
  1. 读写锁
    • 策略:如果共享资源读操作远多于写操作,可以使用读写锁(如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);
}
  1. 无锁数据结构
    • 策略:采用无锁数据结构,如无锁队列(std::atomic<>相关实现),这些数据结构通过原子操作实现线程安全,避免了传统锁的竞争开销。
    • 举例:使用std::atomic<int>实现一个简单的计数器:
std::atomic<int> counter(0);

void incrementCounter() {
    counter++;
}

int getCounterValue() {
    return counter.load();
}

结合条件变量和互斥锁减少不必要的锁竞争

  1. 原理:条件变量用于线程间的同步,当某个条件满足时通知等待的线程。结合互斥锁,线程在等待条件变量时可以释放互斥锁,避免一直持有锁导致其他线程无法访问共享资源,从而减少锁竞争。
  2. 示例代码
#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,只有当条件满足(readytrue或队列不为空)时才重新获取锁并处理数据,从而减少了不必要的锁竞争。