MST

星途 面试题库

面试题:C++ STL迭代器begin的常量迭代特性在复杂场景下的应用与优化

在一个涉及多线程并发访问的C++程序中,有一个`std::list`容器,在不同线程中可能会频繁调用`begin()`获取常量迭代器进行遍历操作。但发现偶尔会出现数据不一致的情况,试分析可能的原因,并提出至少两种基于`begin()`常量迭代特性的优化方案,保证数据的一致性和遍历的高效性。
16.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

可能原因

  1. 未加同步控制:多线程同时访问std::list,没有使用锁等同步机制,导致在一个线程遍历过程中,另一个线程对std::list进行修改(如插入、删除元素),破坏了迭代器的有效性,进而造成数据不一致。
  2. 缓存一致性问题:现代多核处理器存在缓存一致性协议,不同线程对std::list数据的缓存可能不同步,当一个线程修改数据后,其他线程的缓存未能及时更新,在遍历过程中可能获取到旧数据。

优化方案

  1. 使用互斥锁(std::mutex
    #include <iostream>
    #include <list>
    #include <mutex>
    #include <thread>
    
    std::list<int> myList;
    std::mutex listMutex;
    
    void traverseList() {
        std::unique_lock<std::mutex> lock(listMutex);
        for (auto it = myList.begin(); it != myList.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << std::endl;
    }
    
    void modifyList() {
        std::unique_lock<std::mutex> lock(listMutex);
        myList.push_back(1);
        myList.push_back(2);
    }
    
    int main() {
        std::thread t1(traverseList);
        std::thread t2(modifyList);
    
        t1.join();
        t2.join();
    
        return 0;
    }
    
    在此方案中,在遍历和修改std::list前加锁,确保同一时间只有一个线程能访问std::list,保证数据一致性。遍历效率上,由于加锁会带来一定开销,但能有效保证数据一致性。
  2. 读写锁(std::shared_mutex
    #include <iostream>
    #include <list>
    #include <shared_mutex>
    #include <thread>
    
    std::list<int> myList;
    std::shared_mutex listMutex;
    
    void traverseList() {
        std::shared_lock<std::shared_mutex> lock(listMutex);
        for (auto it = myList.begin(); it != myList.end(); ++it) {
            std::cout << *it << " ";
        }
        std::cout << std::endl;
    }
    
    void modifyList() {
        std::unique_lock<std::shared_mutex> lock(listMutex);
        myList.push_back(1);
        myList.push_back(2);
    }
    
    int main() {
        std::thread t1(traverseList);
        std::thread t2(modifyList);
    
        t1.join();
        t2.join();
    
        return 0;
    }
    
    读写锁允许多个线程同时进行读操作(遍历),只有在写操作(修改std::list)时才独占锁。在遍历频繁、写操作较少的场景下,能提高遍历的并发性能,同时保证数据一致性。相比互斥锁,读操作的效率更高,因为读操作之间不需要等待锁的释放。