21xrx.com
2024-11-05 16:35:05 Tuesday
登录
文章检索 我的文章 写文章
C++队列queue多线程实现
2023-07-05 04:29:55 深夜i     --     --
C++ 队列(queue) 多线程(multithreading) 实现(implementation)

队列是计算机科学中经典的数据结构之一,它允许我们按照先进先出(FIFO)的顺序存储元素,并在队列尾部插入元素,在队列头部删除元素。在C++中,实现队列最常见的方式是使用STL库中的队列(queue)数据结构。

单线程环境下,队列的实现是非常简单的,但在多线程环境下,队列的实现就变得更加复杂。在多线程环境下,为了保证线程安全,需要对队列进行一些额外的操作。

使用C++队列queue实现多线程的方式有很多种,下面介绍其中两种方法:

1. 互斥锁和条件变量

这是最常见的一种方法,即使用互斥锁来保证线程的安全性,在队列中插入和删除元素的过程中加锁,这样就保证了同时只有一个线程可以访问队列。当队列为空时,等待条件变量通知,当队列不为空时,唤醒这些线程。

示例代码:


#include<queue>

#include<mutex>

#include<condition_variable>

using namespace std;

template<typename T>

class ThreadsafeQueue {

private:

  queue<T> q;

  mutex mtx;

  condition_variable cv;

public:

  ThreadsafeQueue() = default;

  void push(T t) {

    lock_guard<mutex> lock(mtx);

    q.push(t);

    cv.notify_one();

  }

  bool try_pop(T& t) {

    lock_guard<mutex> lock(mtx);

    if (q.empty())

      return false;

    

    t = q.front();

    q.pop();

    return true;

  }

  void wait_and_pop(T& t) {

    unique_lock<mutex> lock(mtx);

    cv.wait(lock, [this] {return !q.empty(); });

    t = q.front();

    q.pop();

  }

};

2. 无锁队列

面对高并发的情况,使用锁会使性能大大降低,一种更好的选择是使用无锁队列,该队列可以保证线程安全。当一个线程想要入队或出队时,该线程就会比较并交换数据结构的元素。

示例代码:


#include<atomic>

#include<pthread.h>

using namespace std;

template<class T>

class lock_free_queue

{

private:

  struct node

  {

    T data;

    node* next;

  };

  atomic<node*> head;

  atomic<node*> tail;

public:

  lock_free_queue()

  {

    node* null_node = new node;

    null_node->data = 0;

    null_node->next = nullptr;

    head.store(null_node, memory_order_relaxed);

    tail.store(head.load(memory_order_relaxed), memory_order_relaxed);

  }

  ~lock_free_queue()

  {

    node* old_head;

    while (head.load(memory_order_relaxed) != nullptr)

    {

      old_head = head.load(memory_order_relaxed);

      head.store(old_head->next, memory_order_relaxed);

      delete old_head;

    }

  }

  void push(T value)

  {

    node* new_node = new node;

    new_node->data = value;

    new_node->next = nullptr;

    node* prev_tail = tail.load(memory_order_relaxed);

    node* update_tail = prev_tail;

    while (true)

    {

      node* null_node = nullptr;

      if (prev_tail == tail.load(memory_order_relaxed))

      {

        if (prev_tail->next == nullptr)

        {

          if (atomic_compare_exchange_weak_explicit(&prev_tail->next, &null_node, new_node, memory_order_release, memory_order_relaxed))

          

            break;

          

        }

        else

        {

          atomic_compare_exchange_weak_explicit(&tail, &prev_tail, prev_tail->next, memory_order_release, memory_order_relaxed);

        }

      }

      prev_tail = tail.load(memory_order_relaxed);

    }

    atomic_compare_exchange_weak_explicit(&tail, &update_tail, new_node, memory_order_release, memory_order_relaxed);

  }

  bool try_pop(T& value)

  {

    while (true)

    {

      node* const current_head = head.load(memory_order_relaxed);

      node* const current_tail = tail.load(memory_order_relaxed);

      node* const first_node = current_head->next;

      if (current_head == head.load(memory_order_acquire))

      {

        if (current_head == current_tail)

        {

          if (first_node == nullptr)

          

            return false;

          

          atomic_compare_exchange_weak_explicit(&tail, &current_tail, first_node, memory_order_release, memory_order_relaxed);

        }

        else

        {

          value = first_node->data;

          if (atomic_compare_exchange_weak_explicit(&head, &current_head, first_node, memory_order_release, memory_order_relaxed))

          

            delete current_head;

            break;

          

        }

      }

    }

    return true;

  }

};

总之,在使用队列queue实现多线程时,我们应该考虑线程安全性和高效性,根据实际情况选择合适的实现方式,从而保证程序的稳定和高效运行。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复