面试题答案
一键面试可能出现的问题
- 作用域混淆:程序员可能会错误地访问全局变量而非局部变量,或者反之,导致程序逻辑错误。例如,在函数内部本想操作局部变量,却因疏忽未加局部变量声明而操作了全局变量。
- 线程同步问题:多个线程同时访问和修改共享的全局变量,可能导致数据竞争。比如,一个线程读取全局变量的值,另一个线程同时修改该值,会使第一个线程读取到不一致的数据,导致程序出现不可预测的结果。
解决方案及优缺点
- 使用线程局部存储(TLS)
- 实现方式:在C++ 11及之后版本,可以使用
thread_local
关键字声明变量,该变量在每个线程中都有独立的实例,避免了全局变量的共享。例如:thread_local int localVar;
- 优点:彻底避免了全局变量在多线程间的共享,从而避免数据竞争问题。而且语法简单,对现有代码侵入性小,只需在变量声明前添加
thread_local
关键字。 - 缺点:如果需要在不同线程间共享某些数据,这种方式就无法满足需求。并且它依赖于编译器对
thread_local
的支持,在较老的编译器中可能不被支持。
- 实现方式:在C++ 11及之后版本,可以使用
- 使用互斥锁(Mutex)
- 实现方式:在访问共享全局变量时,使用互斥锁来保护该变量。例如,使用C++ 标准库中的
std::mutex
:
- 实现方式:在访问共享全局变量时,使用互斥锁来保护该变量。例如,使用C++ 标准库中的
std::mutex globalVarMutex;
int globalVar;
void threadFunction() {
std::lock_guard<std::mutex> lock(globalVarMutex);
// 访问和修改 globalVar
}
- **优点**:能有效防止数据竞争,通过锁定互斥锁,同一时间只有一个线程可以访问共享全局变量。这种方式通用性强,适用于多种场景,对不同线程间需要共享数据的情况有很好的支持。
- **缺点**:加锁和解锁操作会带来额外的性能开销,尤其是在高并发场景下,频繁的加锁解锁可能成为性能瓶颈。而且如果使用不当,例如死锁(多个线程相互等待对方释放锁),会导致程序挂起。
3. 使用原子操作(Atomic Operations)
- 实现方式:对于一些简单的数据类型(如int
、bool
等),可以使用C++ 标准库中的原子类型,如std::atomic<int>
。例如:
std::atomic<int> globalVar;
void threadFunction() {
int localCopy = globalVar.load(); // 读取全局变量
// 一些操作
globalVar.store(localCopy + 1); // 修改全局变量
}
- **优点**:原子操作通常比使用互斥锁更高效,因为它们在硬件层面保证了操作的原子性,不需要像互斥锁那样进行上下文切换等开销较大的操作。适用于简单数据类型的原子读写操作。
- **缺点**:功能相对有限,只能对简单数据类型和特定的操作提供原子性保证。对于复杂的数据结构或操作,原子类型无法满足需求,仍需要使用互斥锁等机制。