基于CAS的无锁队列

基于CAS实现的自旋锁

#ifndef _X_FREE_LOCK_H
#define _X_FREE_LOCK_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>

typedef struct
{
    int m_lock;
    inline void spinlock_init()
    {
        m_lock = 0;
    }
    inline void spinlock_lock()
    {
        while(!__sync_bool_compare_and_swap(&m_lock, 0, 1)) {}
    }
    inline void spinlock_unlock()
    {
        __sync_lock_release(&m_lock);
    }
} spinlock_t; //自旋锁,当长时间执行不成功,会对CPU带来较大的开销,故一般应用在上锁处理业务时间较短的场景

class AutoReleaseLock
{
public:
    AutoReleaseLock(spinlock_t* lock)
    {
        lock->spinlock_lock();
        m_lock = lock;
    }
    ~AutoReleaseLock()
    {
        m_lock->spinlock_unlock();
    }
private:
    spinlock_t* m_lock;
};

#endif

基于CAS实现的无锁队列

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include "x_freeLock.h"
#define SHM_NAME_LEN 128
#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define IS_POT(x) ((x) && !((x) & ((x)-1)))
#define MEMORY_BARRIER __sync_synchronize()

template <class T,bool NOLOCK=false>
class LockFreeQueue
{
public:
    // size:队列大小
    // name:共享内存key的路径名称,默认为NULL,使用数组作为底层缓冲区。
    LockFreeQueue(unsigned int size, const char* name = NULL)
    {
        memset(shm_name, 0, sizeof(shm_name));
        createQueue(name, size);
    }
    ~LockFreeQueue()
    {
        if(shm_name[0] == 0)
        {
            delete [] m_buffer;
            m_buffer = NULL;
        }
        else
        {
            if (munmap(m_buffer, m_size * sizeof(T)) == -1) {
                perror("munmap");
            }
            if (shm_unlink(shm_name) == -1) {
                perror("shm_unlink");
            }
        }
    }
    
    bool isFull()const
    {
#ifdef USE_POT
        return m_head == (m_tail + 1) & (m_size - 1);
#else
	    return m_head == (m_tail + 1) % m_size;
#endif
    }

    bool isEmpty()const
    {
	    return m_head == m_tail;
    }
    unsigned int front()const
    {
	    return m_head;
    }

    unsigned int tail()const
    {
	    return m_tail;
    }

    bool push(const T& value)
    {
        if(NOLOCK)
        {
            m_spinLock.spinlock_lock();
        }
        if(isFull())
        {
            if(NOLOCK)
            {
                m_spinLock.spinlock_unlock();
            }
            return false;
        }
        memcpy(m_buffer + m_tail, &value, sizeof(T));
#ifdef USE_MB
        MEMORY_BARRIER;
#endif

#ifdef USE_POT
        m_tail = (m_tail + 1) & (m_size - 1);
#else
        m_tail = (m_tail + 1) % m_size;
#endif
        if(NOLOCK)
        {
            m_spinLock.spinlock_unlock();
        }
        return true;
    }

    bool pop(T& value)
    {
        if(NOLOCK)
        {
            m_spinLock.spinlock_lock();
        }
        if (isEmpty())
        {
            if(NOLOCK)
            {
                m_spinLock.spinlock_unlock();
            }
            return false;
        }
        memcpy(&value, m_buffer + m_head, sizeof(T));
#ifdef USE_MB
        MEMORY_BARRIER;
#endif

#ifdef USE_POT
        m_head = (m_head + 1) & (m_size - 1);
#else
        m_head = (m_head + 1) % m_size;
#endif
        if(NOLOCK)
        {
            m_spinLock.spinlock_unlock();
        }
        return true;
    }
protected:
    virtual void createQueue(const char* name, unsigned int size)
    {
#ifdef USE_POT
        if (!IS_POT(size))
        {
            size = roundup_pow_of_two(size);
        }
#endif
        m_size = size;
        m_head = m_tail = 0;
        if(name == NULL)
        {
            m_buffer = new T[m_size];
        }
        else
        {
            int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
            if (shm_fd < 0)
            {
                perror("shm_open");
            }
            if (ftruncate(shm_fd, m_size * sizeof(T)) < 0)
            {
                perror("ftruncate");
                close(shm_fd);
            }
            void *addr = mmap(0, m_size * sizeof(T), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
            if (addr == MAP_FAILED)
            {
                perror("mmap");
                close(shm_fd);
            }
            if (close(shm_fd) == -1)
            {
                perror("close");
                exit(1);
            }
            m_buffer = static_cast<T*>(addr);
            memcpy(shm_name, name, SHM_NAME_LEN - 1);
        }
        if(NOLOCK)
        {
            m_spinLock.spinlock_init();
        }
    }
    inline unsigned int roundup_pow_of_two(size_t size)
    {
        size |= size >> 1;
        size |= size >> 2;
        size |= size >> 4;
        size |= size >> 8;
        size |= size >> 16;
        size |= size >> 32;
        return size + 1;
    }

protected:
    char shm_name[SHM_NAME_LEN];
    volatile unsigned int m_head;
    volatile unsigned int m_tail;
    unsigned int m_size;
    spinlock_t m_spinLock;
    T* m_buffer;
};

这里需要注意的是,单生产者和单消费者模型,不需要加锁和原子操作,是否线程安全?
理论是不安全的,但对这种特殊模式的结果是不影响的。


//单消费者单生产者模型,各自为单独线程,不需要加锁和原子操作
//需要注意的点:判满和判空(这两个操作都访问了读写两个索引,这里需要考虑是否线程安全)
//同时访问读写索引,从理论来看是不安全的,但实际并不影响结果
//生产者:执行push操作,需要判满,需要访问读索引,此刻可能消费者正在操作读索引,如果线程不安全,只能执行
//满逻辑,直接return,这并不会导致越界问题。
//消费者:执行pop操作,需要判空,在判空过程中,实际情况是为空的,但生产者对写索引操作造成了线程不安全,
//只能执行不空逻辑,此刻也可以正常消费,因为真的有数据
template <class T>
class SingleQueue : public LockFreeQueue<T,true>
{
public:
    SingleQueue(unsigned int size, const char* name = NULL):LockFreeQueue{size,name}{}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
vcpkg 中使用的无锁队列是一个基于 CAS (Compare and Swap) 操作的实现,它的实现位于 vcpkg 的 `include/vcpkg/fwdlock.h` 头文件中。 无锁队列的实现使用了 C++11 中的 `std::atomic` 类型,它支持原子操作,并且可以避免多线程访问时的竞争问题。在无锁队列中,每个节点包含一个数据项和一个指向下一个节点的指针,节点的插入和删除操作都是通过原子 CAS 操作来实现的。 无锁队列的代码示例: ```c++ template <typename T> class fwdlock { public: fwdlock() : head_(nullptr), tail_(nullptr) {} void push(T data) { node* n = new node(std::move(data)); node* t = tail_.load(std::memory_order_relaxed); do { n->next_ = t; } while (!tail_.compare_exchange_weak(t, n, std::memory_order_release, std::memory_order_relaxed)); } bool try_pop(T& data) { node* h = head_.load(std::memory_order_relaxed); node* t = tail_.load(std::memory_order_acquire); if (h == t) { return false; } node* next = h->next_; data = std::move(next->data_); head_.store(next, std::memory_order_release); delete h; return true; } private: struct node { T data_; node* next_; node(T data) : data_(std::move(data)), next_(nullptr) {} }; std::atomic<node*> head_; std::atomic<node*> tail_; }; ``` 在上述代码中,`fwdlock` 类实现了无锁队列的 push 和 try_pop 操作,其中 push 操作使用了 `compare_exchange_weak` 原子操作,try_pop 操作使用了 `load` 和 `store` 原子操作。使用无锁队列可以有效减少多线程并发访问时的竞争和锁等待,从而提高代码的性能和可伸缩性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

牵着我的猪去看海

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

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

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

打赏作者

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

抵扣说明:

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

余额充值