21xrx.com
2025-03-21 16:23:44 Friday
文章检索 我的文章 写文章
C++并发编程:队列实现
2023-06-29 13:01:29 深夜i     23     0
C++ 并发编程 队列实现 多线程 生产者消费者模式

C++是一种高级编程语言,与其他编程语言一样,它具有一些模板和类可以用于构建队列。队列是一种非常有用的数据结构,经常被用于在并发编程中,以及在许多其他应用程序中。

队列可以看作是一个“先进先出”的集合,其中最先进入队列的元素是最先被取出的。在C++中,队列可以使用STL模板库中的queue类来实现。但是,STL的队列在多线程的情况下可能不是线程安全的,因此需要额外的工作来实现线程安全的队列。

要实现线程安全的队列,我们可以使用互斥锁/临界区。当一个线程需要访问队列时,它会先请求一个锁,如果锁不可用,则该线程会被阻止,并等待锁可用。如果锁可用,该线程就可以使用队列,并在完成任务后释放锁。

以下是一个使用互斥锁/临界区来实现线程安全队列的示例代码:

#include <queue>
#include <mutex>
#include <condition_variable>
template<typename T>
class threadsafe_queue {
private:
  std::queue<T> q;
  mutable std::mutex m;
  std::condition_variable cv;
public:
  threadsafe_queue() = default;
  threadsafe_queue(const threadsafe_queue&) = delete;
  threadsafe_queue& operator=(const threadsafe_queue&) = delete;
  void push(const T& value) {
    std::lock_guard<std::mutex> lock(m);
    q.push(value);
    cv.notify_one();
  }
  void wait_and_pop(T& value) {
    std::unique_lock<std::mutex> lock(m);
    cv.wait(lock, [this] { return !q.empty(); });
    value = std::move(q.front());
    q.pop();
  }
  std::shared_ptr<T> wait_and_pop() {
    std::unique_lock<std::mutex> lock(m);
    cv.wait(lock, [this] { return !q.empty(); });
    auto res = std::make_shared<T>(std::move(q.front()));
    q.pop();
    return res;
  }
  bool try_pop(T& value) {
    std::lock_guard<std::mutex> lock(m);
    if (q.empty())
      return false;
    
    value = std::move(q.front());
    q.pop();
    return true;
  }
  std::shared_ptr<T> try_pop() {
    std::lock_guard<std::mutex> lock(m);
    if (q.empty()) {
      return std::shared_ptr<T>();
    }
    auto res = std::make_shared<T>(std::move(q.front()));
    q.pop();
    return res;
  }
  bool empty() const {
    std::lock_guard<std::mutex> lock(m);
    return q.empty();
  }
};

此代码使用了std::queue、std::mutex和std::condition_variable来实现一个线程安全的队列。std::queue是C++ STL库中的一个容器适配器,它提供了一个很好的数据结构来实现队列。std::mutex和std::condition_variable是用来实现线程同步和互斥的两个关键工具。

该线程安全队列实现提供了五个基本函数:

1. push() – 将一个元素压入队列;

2. wait_and_pop() – 获取队列中的第一个元素,并且在队列非空时等待;

3. try_pop() – 尝试获取队列中的第一个元素。如果队列已经为空,则该函数不阻塞,并且返回一个空指针;

4. empty() – 检查队列是否为空;

5. wait_and_pop()的重载版本 – 它返回一个存储队列第一个元素副本的std::shared_ptr,从而避免了使用引用参数。

线程安全队列是多线程编程的常见需求。使用互斥锁/临界区来实现一个线程安全的队列并不难,但需要小心地处理并发条件,以避免死锁和竞态条件。上面的示例代码可以作为线程安全队列的一个很好的起点。

  
  

评论区