C++的自旋锁(spin_lock)实现:使用atomic_flag类型

自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,因此使用这种锁会造成busy-waiting,也就是CPU不断的进行检测操作,而无法处理其他任务。

本文使用原子类型atomic_flag来实现自旋锁。atomic_flag 一种简单的原子布尔类型,只支持两种操作:

1、test-and-set(): 检测flag是否为true,如果不为true,则设置为true。如果为true,则不进行处理。
2、clear():清除对flag的设置

之所以利用这种类型来实现自旋锁,最重要的点在于对其对象的操作是完全是原子操作。

关于atomic_flag更多的介绍信息可见以下的链接:
1、cplusplush-atomic_flag
2、C++11 并发指南六(atomic 类型详解一 atomic_flag 介绍)

以下是自旋锁的具体实现以及测试代码:

测试使用三个线程同时对一个全局变量进行写操作,在使用自旋锁的情况下,变量的输出完全有序,而在不使用自旋锁的情况下,变量的打印会出现无序的现象。读者可关闭自旋锁进行测试。

#include <iostream>
#include <atomic>
#include <thread>
using namespace std;

// 自旋锁的实现
class spin_lock {
    
    
public:
    spin_lock() = default;
    spin_lock(const spin_lock&) = delete; 
    spin_lock& operator=(const spin_lock) = delete;
    void lock() {
    
       // acquire spin lock
        while (flag.test_and_set()) {
    
    }
    }
    void unlock() {
    
       // release spin lock
        flag.clear();
    }
private:
    atomic_flag flag;
};

int num = 0;
spin_lock splock;
void addFunc() {
    
    
    for (int i = 0; i < 1000; ++i) {
    
    
        splock.lock();
        ++num;
        cout << "num = " << num << endl;
        splock.unlock();
    }
}

int main() {
    
    
    cout << "程序开始执行" << endl;
    thread t1(addFunc);
    thread t2(addFunc);
    thread t3(addFunc);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

另外,上述实际使用中,我们必须手动调用 unlock() 接口,否则其他线程会因为上锁而等待。这里我们可以使用lock_guard()来实现自动调用lock()和unlock(),当lock_guard()对象离开作用域的时候,其析构函数会自动调用unlock()接口。

是不是又智能了一步~~~~

以下是使用lock_guard()管理atomic_flag对象的过程。注意添加mutex头文件。

#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>
using namespace std;

// 自旋锁的实现
class spin_lock {
    
    
public:
    spin_lock() = default;
    spin_lock(const spin_lock&) = delete; 
    spin_lock& operator=(const spin_lock) = delete;
    void lock() {
    
       // acquire spin lock
        while (flag.test_and_set()) {
    
    }
    }
    void unlock() {
    
       // release spin lock
        flag.clear();
    }
private:
    atomic_flag flag;
};

int num = 0;
spin_lock splock;
void addFunc() {
    
    
    for (int i = 0; i < 1000; ++i) {
    
    
        std::lock_guard<spin_lock> lock(splock); // 就是这里不一样!!!
        ++num;
        cout << "num = " << num << endl;
    }
}

int main() {
    
    
    cout << "程序开始执行" << endl;
    thread t1(addFunc);
    thread t2(addFunc);
    thread t3(addFunc);
    t1.join();
    t2.join();
    t3.join();
    return 0;
}

谢谢阅读

参考:C++11实现自旋锁

猜你喜欢

转载自blog.csdn.net/weixin_43869898/article/details/109721333