面试题答案
一键面试可能遇到的问题
- 数据竞争:多个线程同时访问和修改静态函数内部的静态变量时,可能导致数据不一致。例如,一个线程读取静态变量的值,同时另一个线程修改了该值,这会使读取到的数据不准确。
- 重入问题:如果静态函数不是可重入的,在多个线程同时调用时,可能会出现逻辑错误。例如,函数内部使用了静态局部变量来保存中间状态,多个线程调用时可能会互相干扰这个状态。
解决方法
- 使用互斥锁(Mutex):互斥锁可以保证在同一时间只有一个线程能够进入临界区(访问静态变量的代码段)。
- 当一个线程获取到互斥锁时,其他线程必须等待,直到该线程释放互斥锁。
- C++ 标准库提供了
std::mutex
来实现互斥锁功能。
- 原子操作:对于一些简单的数据类型(如整数、指针等),可以使用原子操作。原子操作是不可分割的,不会被其他线程打断,从而避免数据竞争。
- C++ 标准库提供了
<atomic>
头文件,其中定义了各种原子类型和原子操作。例如std::atomic<int>
类型,它的操作都是原子的。
- C++ 标准库提供了
代码示例
- 使用互斥锁保证线程安全的静态函数
#include <iostream>
#include <mutex>
#include <thread>
std::mutex mtx;
class MyClass {
public:
static int count;
static void increment() {
std::lock_guard<std::mutex> lock(mtx);
++count;
}
};
int MyClass::count = 0;
void threadFunction() {
for (int i = 0; i < 1000; ++i) {
MyClass::increment();
}
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(threadFunction);
}
for (auto& thread : threads) {
thread.join();
}
std::cout << "Final count: " << MyClass::count << std::endl;
return 0;
}
在上述代码中,MyClass::increment
是一个静态函数,通过 std::lock_guard<std::mutex>
来自动管理互斥锁的加锁和解锁。std::lock_guard
的构造函数加锁,析构函数解锁,从而保证 ++count
操作在多线程环境下的线程安全。
- 使用原子操作保证线程安全的静态函数
#include <iostream>
#include <atomic>
#include <thread>
class MyClass {
public:
static std::atomic<int> count;
static void increment() {
++count;
}
};
std::atomic<int> MyClass::count = 0;
void threadFunction() {
for (int i = 0; i < 1000; ++i) {
MyClass::increment();
}
}
int main() {
std::thread threads[10];
for (int i = 0; i < 10; ++i) {
threads[i] = std::thread(threadFunction);
}
for (auto& thread : threads) {
thread.join();
}
std::cout << "Final count: " << MyClass::count << std::endl;
return 0;
}
在这个代码中,MyClass::count
被定义为 std::atomic<int>
类型,因此 ++count
操作本身就是线程安全的,不需要额外的锁机制。