面试题答案
一键面试堆和栈在资源竞争、线程安全方面的特性
- 栈:
- 资源竞争:每个线程拥有自己独立的栈空间,栈上的局部变量在不同线程间不会产生资源竞争。
- 线程安全:由于栈的独立性,栈上数据操作通常是线程安全的,无需额外同步机制。
- 堆:
- 资源竞争:堆是所有线程共享的内存区域,多个线程同时对堆上数据进行操作时,容易产生资源竞争。
- 线程安全:堆上数据操作不是线程安全的,需要同步机制(如互斥锁、读写锁等)来确保数据一致性。
多线程场景设计
假设有一个多线程程序,多个线程需要对一个全局的堆上数组进行求和操作,同时每个线程有自己栈上的局部变量用于临时存储部分和。
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex globalMutex;
std::vector<int> globalArray(100);
int globalSum = 0;
void threadFunction(int threadId) {
int localSum = 0;
for (int i = threadId; i < globalArray.size(); i += std::thread::hardware_concurrency()) {
localSum += globalArray[i];
}
std::lock_guard<std::mutex> lock(globalMutex);
globalSum += localSum;
}
int main() {
for (int i = 0; i < globalArray.size(); ++i) {
globalArray[i] = i + 1;
}
std::vector<std::thread> threads;
for (int i = 0; i < std::thread::hardware_concurrency(); ++i) {
threads.emplace_back(threadFunction, i);
}
for (auto& thread : threads) {
thread.join();
}
std::cout << "Global sum: " << globalSum << std::endl;
return 0;
}
同步机制和优化策略
- 同步机制:
- 互斥锁:如上述代码中使用
std::mutex
和std::lock_guard
来保护对全局堆上变量globalSum
的操作,确保同一时间只有一个线程能修改它。 - 读写锁:如果读操作远多于写操作,可以使用读写锁(如
std::shared_mutex
),允许多个线程同时读,写操作时独占。
- 互斥锁:如上述代码中使用
- 优化策略:
- 减少锁的粒度:只在对共享堆数据进行修改时加锁,尽量缩短锁的持有时间。
- 使用无锁数据结构:对于一些简单的数据结构,如无锁队列、无锁哈希表等,可避免锁带来的开销。
不同同步策略对堆和栈使用效率的影响
- 互斥锁:
- 对堆的影响:保证堆数据的线程安全,但由于同一时间只有一个线程能访问,可能导致其他线程等待,降低了堆操作的并发度。
- 对栈的影响:基本无影响,因为栈是线程私有的,无需同步。
- 读写锁:
- 对堆的影响:读操作并发度提高,适合读多写少的场景。但写操作时仍需独占,可能导致读线程等待。
- 对栈的影响:同样基本无影响,不涉及栈的同步。
- 无锁数据结构:
- 对堆的影响:避免了锁的开销,提高了堆操作的并发性能,但实现复杂,可能需要更多的内存和CPU资源。
- 对栈的影响:不涉及栈的操作,无影响。