【C++】实现自旋锁互斥(TAS 算法和 CAS 算法)

自旋锁——代码在循环中“自旋”。

自旋锁(spin lock)是一种非阻塞锁,如果某线程需要获取锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗 CPU 的时间,不停的试图获取锁。

互斥量(mutex)是阻塞锁,当某线程无法获取锁时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放锁后,操作系统会激活那个被挂起的线程,让其投入运行。

因此,多核 CPU 才能用自旋锁。

其他细节,详看下面代码注释。

#ifndef AMYSPINLOCK_H
#define AMYSPINLOCK_H

#include <atomic>

// 采用 std::atomic_flag 实现自旋锁互斥,即 TAS 算法(Test And Set)
class AMySpinLock1
{
public:
    AMySpinLock1() = default;
    AMySpinLock1(const AMySpinLock1&) = delete;
    AMySpinLock1& operator=(const AMySpinLock1&) = delete;
    void lock()
    {
        while(flag.test_and_set(std::memory_order_acquire));
    }
    void unlock()
    {
        flag.clear(std::memory_order_release);
    }

private:
    // std::atomic_flag 类型的对象必须由宏 ATOMIC_FLAG_INIT 初始化,它把标志初始化为置零状态。
    std::atomic_flag flag{ATOMIC_FLAG_INIT};
};

// 采用 std::atomic<bool> 和 compare_exchange_strong 实现自旋锁互斥,即 CAS 算法(Compare And Swap)
// 比较-交换操作是原子类型的编程基石。
// 使用者给定一个期望值,原子变量将它和自身的值比较,如果相等,就存入另一既定的值,否则,更新期望值所属的变量,向它赋予原子变量的值。
// 比较-交换函数返回布尔类型,如果完成了保存动作(前提是两值相等),则操作成功,函数返回 true;反之操作失败,函数返回 false。
class AMySpinLock2
{
public:
    AMySpinLock2() = default;
    AMySpinLock2(const AMySpinLock2&) = delete;
    AMySpinLock2& operator=(const AMySpinLock2&) = delete;
    void lock()
    {
        // 判断 flag 对象封装的 bool 值是否为期望值(false):
        // ① 若 bool 值为 false,与期望值相等,说明自旋锁空闲。此时,flag 对象写入 true,返回 true,即上锁成功。
        // ② 若 bool 值为 true,与期望值不相等,说明自旋锁被锁。此时,while 将一直循环,直到返回 true 为止。
        bool expected = false;
        while(false == flag.compare_exchange_strong(expected, true))
        {
            // 当 compare_exchange_strong 返回 false 时,
            // 证明 expected 与 flag 不相等,此时 expected 为false,flag 为 true;
            // 则将 expected 赋值为 flag 的值,即此时 expected 为 true 了,
            // 因此要修改为 false 后,才能进入下一次循环。
            expected = false;
        }
    }
    void unlock()
    {
        flag.store(false);
    }

private:
    // flag 对象所封装的 bool 值为 false 时,说明自旋锁未被线程占有。
    std::atomic<bool> flag{false};
};

#endif // AMYSPINLOCK_H

测试程序如下:

#include <iostream>
#include <thread>
#include "./lock/amyspinlock.h"

AMySpinLock1 m;
//AMySpinLock2 m;

void testSpinLock()
{
    static int i = 0, j = 0;

    for(int k=0; k<1000; ++k)
    {
        m.lock();
        ++i;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        ++j;
        if(i != j)
            printf("[%d] i:%d, j:%d \n", std::this_thread::get_id(), i, j);
        m.unlock();
    }
}

int main(int argc, char *argv[])
{
    std::cout << "Test Begin...\n";

    std::thread t1(testSpinLock);
    std::thread t2(testSpinLock);

    if(t1.joinable())
        t1.join();
    if(t2.joinable())
        t2.join();

    std::cout << "Test End...\n";

    return 0;
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
自旋锁互斥锁都是用来保护共享资源的锁机制,它们的选择取决于应用场景和需求。 自旋锁适用于锁的持有时间短,竞争激烈的情况下。它会在获取不到锁时不断地循环检查锁的状态,直到获取到锁为止。这样可以避免线程进入睡眠状态,从而提高锁的效率。 互斥锁适用于锁的持有时间长,竞争不激烈的情况下。它会在获取不到锁时将线程进入睡眠状态,直到获取到锁为止。这样可以避免线程占用过多的 CPU 资源。 下面是一个 C++ 的例子,演示了如何使用自旋锁互斥锁: ```cpp #include <iostream> #include <thread> #include <atomic> #include <mutex> // 自旋锁 class SpinLock { public: void lock() { while (flag.test_and_set(std::memory_order_acquire)); } void unlock() { flag.clear(std::memory_order_release); } private: std::atomic_flag flag = ATOMIC_FLAG_INIT; }; // 互斥锁 std::mutex mtx; // 共享资源 int cnt = 0; // 使用自旋锁保护共享资源 void spinLockFunc() { static SpinLock lock; for (int i = 0; i < 1000000; ++i) { lock.lock(); ++cnt; lock.unlock(); } } // 使用互斥锁保护共享资源 void mutexFunc() { for (int i = 0; i < 1000000; ++i) { std::unique_lock<std::mutex> lck(mtx); ++cnt; } } int main() { std::thread t1(spinLockFunc); std::thread t2(spinLockFunc); std::thread t3(mutexFunc); std::thread t4(mutexFunc); t1.join(); t2.join(); t3.join(); t4.join(); std::cout << cnt << std::endl; return 0; } ``` 在这个例子中,我们创建了一个共享资源 `cnt`,并分别使用自旋锁互斥锁来保护它。其中,`spinLockFunc` 和 `mutexFunc` 分别代表使用自旋锁互斥锁的线程函数。 我们通过创建 4 个线程来模拟多线程并发访问共享资源的情况,最后输出 `cnt` 的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值