如何通过 C++11 的 CAS 实现 Valois 的队列

Posted

技术标签:

【中文标题】如何通过 C++11 的 CAS 实现 Valois 的队列【英文标题】:How to implement Valois' Queue by C++11's CAS 【发布时间】:2018-03-17 03:19:57 【问题描述】:

我想通过 C++11 中提供的原子 CAS 来实现 JD Valois 的论文实现无锁队列

例如,Valois 的算法定义了一个Enqueue 函数:

Enqueue(x) 
    q = new Record();
    q->value = x;
    q->next = NULL;

    do 
        p = tail;
     while( ! CAS(p->next, NULL, q) ); // 1

    CAS(tail, p, q); // 2

我写了这样的代码

struct Node 
    T * val;
    Node * next;
;
std::atomic<Node *> head, tail;

void enqueue(T * t) 
    Node * nd = new Node();
    nd->val = t;
    nd->next = nullptr;

    std::atomic<Node *> p;
    do 
        p = tail; // 1
     while ( /* CAS1 */  ! std::atomic_compare_exchange_weak(&(p->next), nullptr, nd) );
    /* CAS2 */ std::atomic_compare_exchange_weak(&tail, p.load(), nd)

然后发现CAS1CAS2这两个cas函数用错了。一方面,p-&gt;next 不是std::atomic 的类型,另一方面,atomic_compare_exchange_weak 的第二个参数expected 需要一个指针。在问题Why do C++11 CAS operations take two pointer parameters? 中,cas 函数会将*expected 设置为当前值,这会导致nullptr 的取消引用。而且stmt 1,p = tail也失败了,因为atomic的operator=被删除了。那么如何根据 JD Valois 的论文实现无锁队列呢?

【问题讨论】:

【参考方案1】:

您正确描述了所有问题。下一步只是修复代码。

struct Node 
    T * val;
    std::atomic<Node *> next;
;
std::atomic<Node *> head, tail;

void enqueue(T * t) 
    Node * nd = new Node();
    nd->val = t;
    nd->next = nullptr;

    Node *p, *next;
    do 
        p = tail.load(); // 1
        next = nullptr;
     while (/* CAS1 */ !std::atomic_compare_exchange_weak(&p->next, &next, nd));
    /* CAS2 */ std::atomic_compare_exchange_weak(&tail, &p, nd);

【讨论】:

以上是关于如何通过 C++11 的 CAS 实现 Valois 的队列的主要内容,如果未能解决你的问题,请参考以下文章

原子变量的对齐

单点登录CAS使用记:实现自定义验证用户登录

单点登录理解

死磕Java并发-----J.U.C之深入分析CAS

通过 UNSAFE 来实现一个 Atomic 的 CAS 辅助类原创

使用 CAS + Spring Security 实现 SSO