(十二)自旋锁
与传统的互斥锁相同,自旋锁也是一种用于保护共享资源的同步机制,不同都是,自旋锁在尝试获取锁时不会立即进入阻塞状态,而是循环检查锁的状态(自旋),“忙等”直到获取到锁为止。
在多线程并发的环境中,线程的阻塞和唤醒通常需要涉及操作系统的调度器,进行线程上下文的切换。这些操作会消耗大量的CPU时间和系统资源。自旋锁通过忙等待的方式,避免了线程阻塞和系统调度器介入,从而减少了这部分开销。但也需要注意,自旋锁适用于临界区较小且短时间内能够获取锁的情况。如果临界区较大,自旋锁会导致线程长时间占用CPU资源,反而降低系统的整体性能。
C++中可以通过原子类型 std::atomic_flag
来实现自旋锁。std::atomic_flag
只有两种状态:被设置(set
)和未被设置(clear
),其主要成员函数包括:
-
clear()
: 将atomic_flag
设为false
。 -
test_and_set()
: 如果atomic_flag
已经被设置,返回true
,否则返回false
并设置atomic_flag
为true
。
clear
和 test_and_set
的内存顺序默认都是 memory_order_seq_cst
。
自旋锁示例代码:
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
// 使用std::atomic_flag实现自旋锁。
class SpinLock {
private:
std::atomic_flag flag = ATOMIC_FLAG_INIT; // 初始化为未设置状态
public:
void lock() {
while (flag.test_and_set()) { // 使用test_and_set原子操作不断尝试获取锁
// 自旋等待锁被释放
}
}
void unlock() {
flag.clear(); // 使用clear原子操作释放锁
}
};
class SpinLockExample {
private:
SpinLock spinlock; // 自旋锁
int counter; // 计数器
public:
SpinLockExample() : counter(0) {}
void increment() {
for (int i = 0; i < 1000; ++i) {
spinlock.lock();
++counter; // 临界区操作:锁定期间递增计数器
spinlock.unlock();
}
}
int getCounter() const {
return counter;
}
};
void threadFunction(SpinLockExample& example) {
example.increment();
}
int main() {
SpinLockExample example;
std::vector<std::thread> threads;
// 启动多个线程调用increment方法。
for (int i = 0; i < 10; ++i) {
threads.push_back(std::thread(threadFunction, std::ref(example)));
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final counter value: " << example.getCounter() << std::endl;
return 0;
}
结果:
Final counter value: 10000