MST

星途 面试题库

面试题:C++ STL 迭代器 end 范围扩展在多线程环境下的应用与挑战

在多线程环境中使用 C++ STL 容器和迭代器时,对 end 迭代器的迭代范围进行扩展会面临一些特殊的挑战。例如,不同线程可能同时访问和修改容器,导致迭代器失效。请阐述你在这种场景下对 end 迭代器迭代范围扩展的设计思路,包括如何保证线程安全,如何处理迭代器失效问题,以及如何优化性能。同时,请给出一个较为完整的多线程代码示例,展示如何在实际应用中实现这些功能。
44.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 保证线程安全
    • 使用互斥锁(std::mutex)对容器的访问和修改进行保护。在访问或修改容器前锁定互斥锁,操作完成后解锁。
    • 考虑使用读写锁(std::shared_mutex),如果读取操作远多于写入操作,读操作时可以多个线程同时进行,提高并发性能。
  2. 处理迭代器失效问题
    • 在每次使用迭代器前,检查其有效性。可以通过保存容器的大小,在使用迭代器时对比当前容器大小是否改变来判断迭代器是否失效。
    • 当迭代器失效时,重新获取迭代器。例如,在修改容器后,重新调用begin()end()获取新的迭代器。
  3. 优化性能
    • 减少锁的粒度,尽量缩小锁定互斥锁的代码块范围,减少线程等待时间。
    • 使用无锁数据结构替代 STL 容器,如果应用场景允许。例如,使用boost::lockfree库中的无锁队列等数据结构。

多线程代码示例

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <shared_mutex>

std::vector<int> data;
std::shared_mutex dataMutex;

void writeData(int value) {
    std::unique_lock<std::shared_mutex> lock(dataMutex);
    data.push_back(value);
}

void readData() {
    std::shared_lock<std::shared_mutex> lock(dataMutex);
    auto oldSize = data.size();
    for (auto it = data.begin(); it != data.end(); ++it) {
        if (data.size() != oldSize) {
            // 迭代器失效,重新获取
            lock.unlock();
            std::unique_lock<std::shared_mutex> newLock(dataMutex);
            it = data.begin();
            oldSize = data.size();
            newLock.unlock();
            lock.lock();
        }
        std::cout << *it << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::thread writer1(writeData, 1);
    std::thread writer2(writeData, 2);
    std::thread reader1(readData);
    std::thread reader2(readData);

    writer1.join();
    writer2.join();
    reader1.join();
    reader2.join();

    return 0;
}

在上述代码中:

  • writeData函数用于向data容器中写入数据,使用std::unique_lock锁定dataMutex以保证线程安全。
  • readData函数用于读取data容器中的数据,使用std::shared_lock锁定dataMutex以允许多个线程同时读取。在读取过程中,通过对比容器大小来检查迭代器是否失效,若失效则重新获取迭代器。
  • main函数中创建了两个写入线程和两个读取线程,展示了多线程环境下对data容器的并发访问。