単純なC++高同時セキュリティキュー

4186 ワード

通常のデュアルロックキューは、ヘッダとテールが同時にあるか(すなわち、キューが空であるかどうか)を判断すると、ヘッダロックとテールロックのどちらが追加されるかを判断します.幸いなことに、賢い人は96年にもっと素晴らしいアルゴリズムを思いついた.このアルゴリズムもheadとtailの2つのロックを使用していますが、キューが初期化されたときにheadとtailポインタが空ではなく、空のノードを指す重要な点があります.enqueueの場合はキューの末尾に新しいノードを追加すればよい.Dequeueの場合、ヘッダノードではなくhead->next、すなわちヘッダノードの次のノードを返すのは少し複雑です.
このメソッドリファレンスhttp://www.parallellabs.com/2010/10/25/practical-concurrent-queue-algorithm/
以下は私の参考です.https://blog.csdn.net/mymodian9612/article/details/53608084?utm_source=blogxgwz1
変更された単純なC++(Linuxでも使用可能)高同時セキュリティキュー
 
#ifndef __THREADSAFE_QUEUE2_H_
#define __THREADSAFE_QUEUE2_H_
 
#include 
#include 
#include 
#include 
 
template 
class threadsafe_queue2
{
 
public:
 
    threadsafe_queue2()
    {
        node *nd = new node();
        nd->next = nullptr;
        head = node;
        tail = node;
    }
    threadsafe_queue2(const threadsafe_queue2&) = delete;
    threadsafe_queue2 operator=(const threadsafe_queue2&) = delete;
    ~threadsafe_queue2() = default;
    
    std::shared_ptr try_pop()
    {
        std::unique_lock<:mutex> ulkh(head_mut);
        if(head.get()->next == nullptr)
            return nullptr;
        auto old_head = std::move(head);
        head = std::move(old_head->next);
        return head->data;
    }
    
    
    void push(T &&t)
    {
        std::shared_ptr new_data(std::make_shared(std::forward(t)));
        std::unique_ptr new_tail(new node);
        node *p = new_tail->get();
        {
            std::unique_lock<:mutex> ulkt(tail_mut);
            p->data = new_data;
            p->next = nullptr;
            tail->next = std::move(new_tail);
            tail = p;
        }
        data_cond.notify_one();
    }
    
private:
 
    struct node
    {
        std::shared_ptr data;
        std::unique_ptr next;
    };
    
    std::mutex head_mut;
    std::mutex tail_mut;
    std::unique_ptr head;
    node *tail;
    std::condition_variable data_cond;
    
private:
 
    node* get_tail()
    {
        std::unique_lock<:mutex> ulkt(tail_mut);
        return tail;
    }
 
};
 
template 
class threadsafe_queue2
{
 
public:
 
    threadsafe_queue2()
    {
        node *nd = new node();
        nd->next = nullptr;
        head = node;
        tail = node;
    }
    threadsafe_queue2(const threadsafe_queue2&) = delete;
    threadsafe_queue2 operator=(const threadsafe_queue2&) = delete;
    
    ~threadsafe_queue2()
    {
        node *pre;
        for (; head->next != nullptr;)
        {
            pre = head;
            head = head->next;
            delete pre;
        }
        delete head;
    }
    
    T* try_pop()
    {
        node *old_head = nullptr;
        {
            std::unique_lock<:mutex> ulkh(head_mut);
            if (head->next == nullptr)
                return nullptr;
            old_head = head;
            head = head->next;
        }
        T *data = head->data;
        delete old_head;
        return data;
    }
    
    void push(T *t)
    {
        node *new_tail = new node;
        {
            std::unique_lock<:mutex> ulkt(tail_mut);
            tail->data = t;
            tail->next = new_tail;
            tail = new_tail;
        }
        data_cond.notify_one();
    }

private:

    struct node
    {
        T *data;
        node *next;
    };
    
    std::mutex head_mut;
    std::mutex tail_mut;
    node *head;
    node *tail;
    std::condition_variable data_cond;

private:

    node* get_tail()
    {
        std::unique_lock<:mutex> ulkt(tail_mut);
        return tail;
    }

};

#endif

家に帰った後、コードは思い出で変更されたので、間違いがあるかもしれません.大体そういう意味です.性能は私がテストしたもので、以前の普通の首尾ロックより10%から20%向上しました.
そうだtry_popを使うときに使う必要がありますが、
  std::shared_ptr data = xxx.try_pop();