无锁栈实现一例

一、何谓无锁队列

无锁队列,顾名思义,即不需要加锁的队列;之所以不需要额外加锁,是因为其本身已经是线程安全的。

二、为什么要在队列中集成线程安全的机制?

这里我想采用对比的方式来讲述。有锁队列,这可能是最简单的一种队列了,比如我们在多线程情况下使用标准STD的deque,那么毫无疑问需要对其加锁。加锁其实是将协调过程交给了操作系统来管理,但无锁队列却是在CPU层面就做到了协调,所以在效率上会高很多。更详细的解释请参见http://www.searchtb.com/2012/10/introduction_to_disruptor.html

三、如何实现?

这里主要是采用ACS。

1. 定义队列。这里由于测试的缘故,队列节点内的数据比较简单。

/* ACS node define. */
typedef struct acs_node_t {
    std::string id;
    int index;
    struct acs_node_t* next;
} acs_node_t;

/* ACS deque define. */
typedef struct acs_deque_t {
    struct acs_node_t head;
    struct acs_node_t* tail;
} acs_deque_t;


2. 定义接口。这里定义了队列初始化,入队列以及出队列三个接口。

void acs_deque_init(acs_deque_t* deq);
int acs_deque_empty(acs_deque_t* deq);
void acs_deque_push(acs_deque_t* deq, acs_node_t* node);
acs_node_t* acs_deque_pop(acs_deque_t* deq);


3.下面是接口的实现。

void acs_deque_init(acs_deque_t* deq)
{
    if (deq) {
        deq->tail = &deq->head;
        deq->head.next = NULL;
    }
}

int acs_deque_empty(acs_deque_t* deq)
{
    if (!deq)
        return 1;
    return deq->head.next == NULL;
}

void acs_deque_push(acs_deque_t* deq, acs_node_t* node)
{
    acs_node_t* q = NULL;
    
    do {
        q = deq->tail;
    } while (InterlockedCompareExchangePointer((PVOID*)&q->next, 0, node) != q->next);

    InterlockedCompareExchangePointer((PVOID*)&deq->tail, q, node);
}

acs_node_t* acs_deque_pop(acs_deque_t* deq)
{
    acs_node_t* q = NULL;
    
    do {
        q = deq->head.next;
        if (q == NULL)
            return NULL;
    } while (InterlockedCompareExchangePointer((PVOID*)&deq->head.next, q, q->next) != deq->head.next);
    
    return q;
}
接口采用了Windows的ACS函数,当然你也可以将其更改为linux版本的ACS函数。

4. 其他代码为测试代码,全部代码为

#include <stdio.h>

#include <string>

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include <deque>

static const int knThreadCount = 4;
static const int knMaxNodeCount = 50000;
static const int knPopedCount = 5000;

/* ACS node define. */
typedef struct acs_node_t {
    std::string id;
    int index;
    struct acs_node_t* next;
} acs_node_t;

/* ACS deque define. */
typedef struct acs_deque_t {
    struct acs_node_t head;
    struct acs_node_t* tail;
} acs_deque_t;

typedef struct DequeData {
    std::string id;
    int index;
} DequeData;

typedef std::deque<DequeData*> StdDeque;

CRITICAL_SECTION g_stdcs;

int g_aes_cost_time = 0;
int g_std_cost_time = 0;

class HRTimer {
public:
    HRTimer();
    ~HRTimer() {}

    double GetFrequency(void);
    void StartTimer(void);
    double StopTimer(void);

private:
    LARGE_INTEGER start_;
    LARGE_INTEGER stop_;
    double frequency_;
};

HRTimer::HRTimer()
    : start_(),
    stop_(),
    frequency_(0.f)
{
    frequency_ = this->GetFrequency();
}

double HRTimer::GetFrequency(void)
{
    LARGE_INTEGER proc_freq;
    if (!::QueryPerformanceFrequency(&proc_freq))
        return 0.f;
    return proc_freq.QuadPart;
}

void HRTimer::StartTimer(void)
{
    HANDLE curth = ::GetCurrentThread();
    DWORD_PTR oldmask = ::SetThreadAffinityMask(curth, 0);
    ::QueryPerformanceCounter(&start_);
    ::SetThreadAffinityMask(curth, oldmask);
}

double HRTimer::StopTimer(void)
{
    HANDLE curth = ::GetCurrentThread();
    DWORD_PTR oldmask = ::SetThreadAffinityMask(curth, 0);
    ::QueryPerformanceCounter(&stop_);
    ::SetThreadAffinityMask(curth, oldmask);
    return ((stop_.QuadPart - start_.QuadPart) / frequency_) * 1000;
}

class AutoHRTimer {
public:
    AutoHRTimer(HRTimer& hrt, const char* name);
    ~AutoHRTimer();

private:
    HRTimer& hrt_;
    const char* name_;
};

AutoHRTimer::AutoHRTimer(HRTimer& hrt, const char* name)
    : hrt_(hrt),
    name_(name)
{
    hrt_.StartTimer();
}

AutoHRTimer::~AutoHRTimer()
{
    double diff = hrt_.StopTimer();
    fprintf(stdout, "%s cost time %f ms\n", name_, diff);
}

HRTimer g_hrtimer;

void acs_deque_init(acs_deque_t* deq);
int acs_deque_empty(acs_deque_t* deq);
void acs_deque_push(acs_deque_t* deq, acs_node_t* node);
acs_node_t* acs_deque_pop(acs_deque_t* deq);

void acs_deque_init(acs_deque_t* deq)
{
    if (deq) {
        deq->tail = &deq->head;
        deq->head.next = NULL;
    }
}

int acs_deque_empty(acs_deque_t* deq)
{
    if (!deq)
        return 1;
    return deq->head.next == NULL;
}

void acs_deque_push(acs_deque_t* deq, acs_node_t* node)
{
    acs_node_t* q = NULL;
    
    do {
        q = deq->tail;
    } while (InterlockedCompareExchangePointer((PVOID*)&q->next, 0, node) != q->next);

    InterlockedCompareExchangePointer((PVOID*)&deq->tail, q, node);
}

acs_node_t* acs_deque_pop(acs_deque_t* deq)
{
    acs_node_t* q = NULL;
    
    do {
        q = deq->head.next;
        if (q == NULL)
            return NULL;
    } while (InterlockedCompareExchangePointer((PVOID*)&deq->head.next, q, q->next) != deq->head.next);
    
    return q;
}

static DWORD AesThreadFunc(void* arg)
{
    acs_deque_t* ad = (acs_deque_t*)arg;

    for (int i = 0; i < knMaxNodeCount; ++i) {
        acs_node_t* an = new acs_node_t;
        an->id = "randid_";
        an->id.push_back((i % 10) + '0');
        an->index = i;
        an->next = NULL;
        acs_deque_push(ad, an);
    }  // for

    return 0;
}

static void TestAcsDeque()
{
    acs_deque_t ad;
    acs_node_t* poped_node = NULL;
    HANDLE th[knThreadCount];

    {
        AutoHRTimer ahr(g_hrtimer, "ACS push 50000");
        acs_deque_init(&ad);
        for (int i = 0; i < knThreadCount; ++i) {
            th[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AesThreadFunc, &ad, 0, NULL);
        }  // for
        ::WaitForMultipleObjects(knThreadCount, th, TRUE, INFINITE);
    }

    {
        // Test pop.
        AutoHRTimer ahr(g_hrtimer, "ACS pop 5000");
        for (int i = 0; i < knPopedCount; ++i) {
            poped_node = acs_deque_pop(&ad);
            delete poped_node;
        }
    }

    {
        AutoHRTimer ahr(g_hrtimer, "ACS free 45000");
        acs_node_t* cur = ad.head.next;
        while (cur != NULL) {
            acs_node_t* tmp = cur;
            cur = cur->next;
            delete tmp;
        }
    }
}

static DWORD StdThreadFunc(void* arg)
{
    StdDeque* deq_list = (StdDeque*)arg;

    for (int i = 0; i < knMaxNodeCount; ++i) {
        DequeData* dd = new DequeData;
        dd->id = "randid_";
        dd->id.push_back((i % 10) + '0');
        dd->index = i;
        EnterCriticalSection(&g_stdcs);
        deq_list->push_back(dd);
        LeaveCriticalSection(&g_stdcs);
    }  // for

    return 0;
}

static void TestLockedDeque()
{
    StdDeque deq_list;
    DequeData* poped_dd = NULL;
    HANDLE th[knThreadCount];
    InitializeCriticalSectionAndSpinCount(&g_stdcs, 2000);

    {
        AutoHRTimer ahr(g_hrtimer, "STD push 50000");
        for (int i = 0; i < knThreadCount; ++i) {
            th[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StdThreadFunc, &deq_list, 0, NULL);
        }  // for
        ::WaitForMultipleObjects(knThreadCount, th, TRUE, INFINITE);
    }

    {
        AutoHRTimer ahr(g_hrtimer, "STD pop 5000");
        for (int i = 0; i < knPopedCount; ++i) {
            poped_dd = deq_list.front();
            deq_list.pop_front();
            delete poped_dd;
        }
    }

    {
        AutoHRTimer ahr(g_hrtimer, "STD free 45000");
        StdDeque::iterator iter = deq_list.begin();
        while (iter != deq_list.end()) {
            DequeData* dd = *iter;
            delete dd;
            ++iter;
        }
        deq_list.clear();
    }

    DeleteCriticalSection(&g_stdcs);
}

int main()
{
    while (1) {
        TestAcsDeque();
        TestLockedDeque();
        Sleep(3000);
        fprintf(stdout, "--------------------------------------\n");
    }

    getchar();

    return 0;
}

5. 将无锁队列同std的有锁队列进行对比,效果如下图




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值