首页 > 编程笔记 > C++笔记 阅读:28

C++自旋锁的2种实现方式(附带实例)

自旋锁是一种在等待释放锁的过程中持续检查锁状态的锁,不释放当前线程的执行权。

自旋锁适用于锁持有时间极短的情况,因为它可以避免线程上下文切换的开销。然而,如果持有锁的时间较长,自旋锁可能导致CPU时间的浪费。

C++ 中使用自旋锁,可以选择使用标准库中的相关功能或者依靠操作系统层面的原语。这里介绍两种常见的方法:

C++11 std::atomic_flag

std::atomic_flag 作为原子类型,专为构建无锁的同步机制而设计。尽管它本身不是锁,但它的这些特性使其成为实现自旋锁的理想选择。

自旋锁是一种忙等锁,其中线程反复检查锁的状态,而不是进入阻塞状态。这在等待时间非常短的场景中非常有效,因为它避免了线程挂起和恢复带来的开销。

实例展示:
#include <atomic>
#include <thread>
#include <iostream>
class SpinLock {
private:
   std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
   void lock() {
       while (flag.test_and_set(std::memory_order_acquire));  // 等待直到锁被释放
   }
   void unlock() {
       flag.clear(std::memory_order_release);
   }
};
void task(SpinLock& spinlock) {
   spinlock.lock();
   // 执行临界区代码
   std::cout << "Thread " << std::this_thread::get_id() << " entered critical section.\n";
   spinlock.unlock();
}
int main() {
   SpinLock spinlock;
   std::thread t1(task, std::ref(spinlock));
   std::thread t2(task, std::ref(spinlock));
   t1.join();
   t2.join();
   return 0;
}
在这段代码中,SpinLock 类使用 std::atomic_flag 来实现锁的功能。锁的获取是通过在循环中不断尝试设置标志直到成功为止,这个过程称为自旋。

POSIX线程库的自旋锁

当我们在使用 UNIX/Linux 系统的时候,可以使用 POSIX 线程库提供的自旋锁。其中 pthread_spinlock_t 是 POSIX 线程库中提供的一种自旋锁实现,专为多线程应用中的轻量级同步而设计。

与传统的互斥锁(如pthread_mutex_t)相比,自旋锁不会使等待锁的线程进入睡眠状态,而是让线程在一个紧密的循环中忙等,直到锁变为可用。这种类型的锁特别适合于锁持有时间非常短的场景,因为它避免了线程上下文切换的开销。

下面是一个使用自旋锁的实例,我们需要包含 <pthread.h> 头文件,并在编译时链接 pthread 库。
#include <iostream>
#include <pthread.h>
// 定义全局变量和自旋锁
int sharedVariable = 0;
pthread_spinlock_t spinlock;
// 线程函数
void *threadFunction(void *arg) {
    int thread_id = *(int*)arg;
    // 加锁
    pthread_spin_lock(&spinlock);
    // 临界区
    std::cout << "Thread " << thread_id << " is incrementing sharedVariable..." << std::endl;
    sharedVariable++;
    // 解锁
    pthread_spin_unlock(&spinlock);
    pthread_exit(NULL);
}
int main() {
    // 初始化自旋锁
    pthread_spin_init(&spinlock, PTHREAD_PROCESS_PRIVATE);
    // 创建线程
    pthread_t thread1, thread2;
    int id1 = 1, id2 = 2;
    pthread_create(&thread1, NULL, threadFunction, &id1);
    pthread_create(&thread2, NULL, threadFunction, &id2);
    // 等待线程结束
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
    // 销毁自旋锁
    pthread_spin_destroy(&spinlock);
    // 输出结果
    std::cout << "sharedVariable = " << sharedVariable << std::endl;
    return 0;
}
这个例子展示了如何使用 POSIX 的 pthread_spinlock_t 实现自旋锁。在使用时需要注意,这种方法依赖于操作系统的支持。

使用自旋锁时应当注意,如果锁持有时间较长,或者系统中有许多线程在竞争同一个锁,使用自旋锁可能会导致 CPU 资源的浪费。在这种情况下,可能需要考虑其他类型的锁,如互斥锁。

相关文章