面试题答案
一键面试可能原因
- 未加同步控制:多线程同时访问
std::list
,没有使用锁等同步机制,导致在一个线程遍历过程中,另一个线程对std::list
进行修改(如插入、删除元素),破坏了迭代器的有效性,进而造成数据不一致。 - 缓存一致性问题:现代多核处理器存在缓存一致性协议,不同线程对
std::list
数据的缓存可能不同步,当一个线程修改数据后,其他线程的缓存未能及时更新,在遍历过程中可能获取到旧数据。
优化方案
- 使用互斥锁(
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
,保证数据一致性。遍历效率上,由于加锁会带来一定开销,但能有效保证数据一致性。 - 读写锁(
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
)时才独占锁。在遍历频繁、写操作较少的场景下,能提高遍历的并发性能,同时保证数据一致性。相比互斥锁,读操作的效率更高,因为读操作之间不需要等待锁的释放。