无锁队列学习

文章介绍了无锁编程的概念,对比了加锁和无锁策略的区别。重点讲解了CAS(CompareAndSwap)原语在无锁编程中的应用,以及如何使用CAS实现一个简单的无锁队列。无锁队列的代码示例展示了如何利用原子操作保证线程安全,同时包含push和pop操作的实现细节。
摘要由CSDN通过智能技术生成

加锁是一种悲观的策略,它总是认为每次访问共享资源的时候总会发生冲突。
无锁是一种乐观的策略,它假设线程访问共享资源不会发生冲突,所以不需要加锁。

1 CAS

核心思想
无锁的策略使用一种比较交换的技术(CAS Compare And Swap)来鉴别线程冲突,一旦检测到冲突产生,就重试当前操作直到没有冲突为止。

CAS是系统原语,CAS操作是一条CPU的原子指令,所以不会有线程安全问题。

template <class T>
bool CAS(T expected, T value) 
{
   if (*addr == expected) 
   {
      *addr = value;
      return true;
   }
   return false;
}
  1. 读取内存地址addr的当前值,将其与期望值expected进行比较。
  2. 如果当前值等于期望值,则将内存地址addr的值更新为新值value。
  3. 如果当前值不等于期望值,则不进行任何操作。
  4. 当前大多数机器都在硬件级实现了这个操作,在 Inter 处理器上这个操作是 CMPXCHG ,因而 CAS 是一个最基础的原子操作。

2 手撸一个无锁队列

  • 头文件
 //
// Created by AlbertZ on 2023/6/8.
//

#ifndef NO_LOCK_QUEUE_MY_LOCK_FREE_QUEUE_H
#define NO_LOCK_QUEUE_MY_LOCK_FREE_QUEUE_H

#include <atomic>
#include <iostream>

template<typename T,size_t size>
class MyLockFreeQueue{
    static_assert(std::is_trivial<T>::value, "The type T must be trivial");
    static_assert(size > 2, "Buffer size must be bigger than 2");
public:
    MyLockFreeQueue();
    ~MyLockFreeQueue();
    bool push(const T& data);
    bool pop(T& data);
private:
    T* _data = nullptr;
    std::atomic<int> _head;
    std::atomic<int> _tail;
};

// 构造函数
template <typename T, size_t size>
MyLockFreeQueue<T, size>::MyLockFreeQueue() : _head(0), _tail(0) {
    _data = new T[size];
}
// 析构函数
template <typename T, size_t size>
MyLockFreeQueue<T, size>::~MyLockFreeQueue(){
    delete[] _data;
}

// push
template<typename T, size_t size>
bool MyLockFreeQueue<T,size>::push(const T& data){
    const size_t w = _tail.load(std::memory_order_relaxed);
    size_t w_next = w + 1;
    if (w_next == size) {
        w_next = 0U;
    }
    /* Full check  */
    const size_t r = _head.load(std::memory_order_acquire);
    if (w_next == r) {
        return false;
    }

    /* Place the element */
    _data[w] = data;

    /* Store the next write index */
    _tail.store(w_next, std::memory_order_release);
    return true;
}

// pop
template <typename T, size_t size>
bool MyLockFreeQueue<T, size>::pop(T& data){
    size_t r = _head.load(std::memory_order_relaxed);
    const size_t w = _tail.load(std::memory_order_acquire);

    /* Empty check */
    if (r == w) {
        return false;
    }

    /* Remove the element */
    data = _data[r];

    /* Increment the read index */
    r++;
    if (r == size) {
        r = 0U;
    }
    /* Store the read index */
    _head.store(r, std::memory_order_release);

    return true;
}

#endif //NO_LOCK_QUEUE_MY_LOCK_FREE_QUEUE_H

  • 测试函数
//
// Created by AlbertZ on 2023/6/8.
//

#include <iostream>
#include <thread>
#include <future>
#include "my_lock_free_queue.h"

MyLockFreeQueue<int , 50> queue;

void PushThread() {
    for (int i = 0; i < 100; ++i) {
        queue.push(i); // 将数字 1 到 20 依次加入队列
//        std::cout << "Push " << i << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void PopThread() {
    for(int i = 0; i < 100; i++) {
        int value;
        if (queue.pop(value)) {
            std::cout << std::this_thread::get_id() << " Popped: " << value << std::endl; // 输出弹出的值
        } else {
            std::cout << "Queue is empty" << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }
}

int main() {
    auto future_push = std::async(std::launch::async,PushThread);
    auto future_pop1 = std::async(std::launch::async,PopThread);
    auto future_pop2 = std::async(std::launch::async,PopThread);
    std::cout << "Hello async!" << std::endl;
    std::cin.get();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强大的RGG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值