MST

星途 面试题库

面试题:C++多线程资源竞争与锁机制优化

假设有多个线程同时访问和修改一个共享的`std::vector<int>`容器,为了避免数据竞争,通常会使用锁机制。请阐述如何使用`std::mutex`和`std::unique_lock`来实现对该容器的安全访问,并提出至少一种优化方案以提高多线程环境下的性能。
21.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 使用std::mutexstd::unique_lock实现安全访问
    • 首先定义一个std::mutex对象,用于保护std::vector<int>容器。
    • 在对std::vector<int>进行访问或修改的函数中,使用std::unique_lock<std::mutex>来锁定std::mutexstd::unique_lock提供了更灵活的锁管理方式,比如可以在构造时锁定,在析构时自动解锁。 示例代码如下:
#include <iostream>
#include <vector>
#include <mutex>
#include <thread>

std::vector<int> sharedVector;
std::mutex vectorMutex;

void addElement(int num) {
    std::unique_lock<std::mutex> lock(vectorMutex);
    sharedVector.push_back(num);
}

void printVector() {
    std::unique_lock<std::mutex> lock(vectorMutex);
    for (int num : sharedVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
}
  • 然后可以在多线程环境中调用这些函数:
int main() {
    std::thread t1(addElement, 1);
    std::thread t2(addElement, 2);
    std::thread t3(printVector);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}
  1. 优化方案
    • 减少锁的粒度
      • 如果std::vector<int>容器中的操作可以被细分为独立的部分,那么可以对不同部分使用不同的锁。例如,如果只是对std::vector<int>的前半部分和后半部分分别进行独立操作,可以定义两个std::mutex,一个保护前半部分,一个保护后半部分。这样不同线程就可以同时操作不同部分,提高并发性能。
    • 读写锁
      • 如果对std::vector<int>的操作读多写少,可以使用读写锁(如std::shared_mutex)。读操作可以同时进行,只有写操作需要独占锁。在C++ 17及以上,std::shared_mutex可以和std::unique_lock(通过指定合适的锁策略,如std::defer_lock等)以及std::shared_lock配合使用。例如:
#include <iostream>
#include <vector>
#include <shared_mutex>
#include <thread>

std::vector<int> sharedVector;
std::shared_mutex vectorMutex;

void readElement() {
    std::shared_lock<std::shared_mutex> lock(vectorMutex);
    // 进行读操作
    for (int num : sharedVector) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
}

void writeElement(int num) {
    std::unique_lock<std::shared_mutex> lock(vectorMutex);
    // 进行写操作
    sharedVector.push_back(num);
}
  • 无锁数据结构
    • 考虑使用无锁数据结构来替代std::vector<int>,如std::atomic相关的数据结构或者一些第三方的无锁数据结构库。无锁数据结构通过使用原子操作避免了锁带来的性能开销,但实现和使用通常更复杂。例如,在某些场景下,可以使用std::atomic<int>数组来模拟类似std::vector<int>的功能。