同步队列
介绍
实现过程
介绍
1.本篇文章旨在通过c++11新特新实现基本的参数模板化同步队列,此队列用以支持基本生产者、消费者应用
2.主要通过原子变量、条件变量、互斥锁、链表容器等实现
3.逐步实现此同步队列,并支持基本的生产者、消费者例程
实现过程
变量定义
// 引用使用到的标准库头文件
#include <atomic> // c++11标准提供原子操作相关,可保证多线程数据访问的安全
#include <condition_variable> // 条件变量,可用于实现多线程之间的同步操作
#include <list> // 双向列表队列
#include <mutex> // c++11标准提供的互斥锁,可用于多线程间数据访问保护操作
template <typename T> // 参数类型模板化
class SyncQueue // 同步队列类
{
... // 省略其它实现
private:
std::condition_variable m_cv; // 同步原语
std::list<T> m_queue; // 数据队列
std::mutex m_mutex; // 互斥锁,保护数据队列多线程访问
std::atomic_bool m_stop; // 停止队列,拒绝接收数据
};
成员函数
// 主要提供以下类成员函数
// SyncQueue、~SyncQueue、Push、Take、Stop、Size、IsEmpty等
SyncQueue() : m_stop(false) {} // 构造函数
virtual ~SyncQueue() { Stop(true); } // 析构函数
void Push(const T &v) { // 添加数据到类内部数据队列
if (m_stop) // 停止添加数据
return;
{ // 减小锁的粒度
std::lock_guard<std::mutex> locker(m_mutex); // 保护数据
m_queue.emplace_back(v); // 添加数据
}
m_cv.notify_one(); // 通知任意一个其它同步阻塞的线程,有数据可读
}
void Push(T &&v) { // 添加数据到类内部数据队列,形参以右值引用方式传输
if (m_stop)
return;
{
std::lock_guard<std::mutex> locker(m_mutex);
m_queue.emplace_back(v);
}
m_cv.notify_one();
}
void Take(std::list<T> *ret) { // 获取类内部队列数据,一次获取所有
if (ret == nullptr) // 判断是否空指针
return;
{
std::unique_lock<std::mutex> locker(m_mutex);
// 同步阻塞当前线程,当通知后唤醒线程,继续执行之后的流程,若lambda函数返回 false,则继续阻塞当前线程
m_cv.wait(locker, [this] { return m_stop || !m_queue.empty(); });
}
if (m_stop)
return;
std::lock_guard<std::mutex> locker(m_mutex);
ret->swap(m_queue); // 获取所有数据
}
void Take(T *ret) { // 获取某一个数据
if (ret == nullptr)
return;
{
std::unique_lock<std::mutex> locker(m_mutex);
m_cv.wait(locker, [this] { return m_stop || !m_queue.empty(); });
}
if (m_stop)
return;
std::lock_guard<std::mutex> locker(m_mutex);
*ret = m_queue.front(); // 获取第一个数据
m_queue.erase(m_queue.begin()); // 删除已获取的数据
}
bool Stop(bool stop) { // 停止队列
m_stop.store(stop);
if (m_stop)
m_cv.notify_all(); // 唤醒所有同步等待线程
}
int Size() { // 获取队列数据数量
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
bool IsEmpty() { // 判断队列是否为空
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
完整实现
// SyncQueue.hpp
#ifndef SYNCQUEUE_H
#define SYNCQUEUE_H
#include <atomic>
#include <condition_variable>
#include <list>
#include <mutex>
namespace que {
template <typename T> class SyncQueue {
public:
SyncQueue() : m_stop(false) {}
virtual ~SyncQueue() { Stop(true); }
void Push(const T &v) {
if (m_stop)
return;
{
std::lock_guard<std::mutex> locker(m_mutex);
m_queue.emplace_back(v);
}
m_cv.notify_one();
}
void Push(T &&v) {
if (m_stop)
return;
{
std::lock_guard<std::mutex> locker(m_mutex);
m_queue.emplace_back(v);
}
m_cv.notify_one();
}
void Take(std::list<T> *ret) {
if (ret == nullptr)
return;
{
std::unique_lock<std::mutex> locker(m_mutex);
m_cv.wait(locker, [this] { return m_stop || !m_queue.empty(); });
}
if (m_stop)
return;
std::lock_guard<std::mutex> locker(m_mutex);
ret->swap(m_queue);
}
void Take(T *ret) {
if (ret == nullptr)
return;
{
std::unique_lock<std::mutex> locker(m_mutex);
m_cv.wait(locker, [this] { return m_stop || !m_queue.empty(); });
}
if (m_stop)
return;
std::lock_guard<std::mutex> locker(m_mutex);
*ret = m_queue.front();
m_queue.erase(m_queue.begin());
}
bool Stop(bool stop) {
m_stop.store(stop);
if (m_stop)
m_cv.notify_all();
}
int Size() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
bool IsEmpty() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
private:
std::condition_variable m_cv;
std::list<T> m_queue;
std::mutex m_mutex;
std::atomic_bool m_stop;
};
} // namespace que
#endif // SYNCQUEUE_H
}
例程
- 生产者、消费者模式,数据同步
#include "SyncQueue.hpp"
#include <thread>
#include <chrono>
#include <iostream>
void TestSyncQueue() {
que::SyncQueue<int> sync;
std::thread t1([&sync] {
thread_local int i = 0;
while (true) {
sync.Push(std::move(i++));
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});
std::thread t2([&sync] {
int val = -1;
while (true) {
sync.Take(&val);
std::cout << val << " - " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});
std::thread t3([&sync] {
int val = -1;
while (true) {
sync.Take(&val);
std::cout << val << " - " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});
std::thread t4([&sync] {
int val = -1;
while (true) {
sync.Take(&val);
std::cout << val << " - " << std::this_thread::get_id() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
});
t1.join();
t2.join();
t3.join();
t4.join();
}
int main(int argc, char *argv[]) {
TestSyncQueue();
return 0;
}
输出结果:
0 - 139746321880640
1 - 139746330273344
2 - 139746313487936
3 - 139746321880640
4 - 139746330273344
5 - 139746330273344
6 - 139746321880640
7 - 139746321880640
8 - 139746330273344
9 - 139746313487936
10 - 139746321880640
- 异步任务模式
#include "SyncQueue.hpp"
#include <functional>
...
using Task = std::function<void()>; // 函数包装器,抽象任务类型
class ExecuteTask final {
public:
...
void Push(const Task &task) {
m_queue.Push(task);
}
void Push(Task &&task) {
m_queue.Push(task);
}
private:
que::SyncQueue<Task> m_queue;
// std::thread* m_thread; // 此处使用线程或线程池,同步等待任务,异步执行
};
本文介绍了如何使用C++11的新特性,如原子变量、条件变量和互斥锁,创建一个模板化的同步队列,支持生产者和消费者模式。详细展示了构造、析构函数以及主要的成员函数实现,包括Push、Take、Stop等操作。
1542

被折叠的 条评论
为什么被折叠?



