Я произвел простую реализацию безблокирующей (lockfree) очереди, используя новый std::atomic в C ++ 11. Я не понимаю, что я здесь делаю не так.

#include <atomic>

template<typename T>
class lockless_queue
{
public:
    template<typename DataType>
    struct node
    {
        node(const DataType& data)
          : data(data), next(nullptr) {}
        DataType data;
        node* next;
    };

    lockless_queue()
      : head_(nullptr) {}

    void produce(const T &data)
    {
        node<T>* new_node = new node<T>(data);
        // put the current value of head into new_node->next
        new_node->next = head_.load(std::memory_order_relaxed);
        // now make new_node the new head, but if the head
        // is no longer what's stored in new_node->next
        // (some other thread must have inserted a node just now)
        // then put that new head into new_node->next and try again
        while(!std::atomic_compare_exchange_weak_explicit(
            &head_,
            &new_node->next,
            new_node,
            std::memory_order_release,
            std::memory_order_relaxed)) {}
    }

    node<T>* consume_all()
    {
        // Reset queue and return head atomically
        return head_.exchange(nullptr, std::memory_order_consume);
    }
private:
    std::atomic<node<T>*> head_;
};

// main.cpp
#include <iostream>

int main()
{
    lockless_queue<int> s;
    s.produce(1);
    s.produce(2);
    s.produce(3);
    auto head = s.consume_all();
    while (head)
    {
        auto tmp = head->next;
        std::cout << tmp->data << std::endl;
        delete head;
        head = tmp;
    }
}

И мой вывод:

2
1
Segmentation fault (core dumped)

Можно мне несколько указателей, где искать, или указание на то, что я делаю неправильно?

Благодарность!

5
Amir Taaki 20 Дек 2013 в 22:57

2 ответа

Лучший ответ

Вы разыменовываете tmp вместо head:

while (head)
    {
        auto tmp = head->next;
        std::cout << tmp->data << std::endl;
        delete head;
        head = tmp;
    }

Должно быть:

while (head)
    {
        std::cout << head->data << std::endl;
        auto tmp = head->next;
        delete head;
        head = tmp;
    }

Вот почему 3 не появляется в вашем выводе, а Segmentation fault появляется.

3
Casey 20 Дек 2013 в 19:24

У вас есть еще одна ошибка в вашем коде, которая не появится, пока вы не начнете пытаться выполнять параллельные постановки в очередь. Если ваш compare_exchange_weak_explicit выходит из строя, это означает, что другому потоку удалось изменить указатель head, и поэтому, прежде чем вы сможете снова попробовать свой CAS, вам необходимо повторно загрузить новое значение head указатель на ваш new_node->next. Следующее поможет:

    while(!std::atomic_compare_exchange_weak_explicit(
        &head_,
        &new_node->next,
        new_node,
        std::memory_order_release,
        std::memory_order_relaxed)) {
        new_node->next = head_.load(std::memory_order_relaxed);
    }
2
Ken P 22 Дек 2013 в 17:42