目录
一、单体类消息队列概念
c++线程之间通信方式多种多样,诸如全局变量、回调函数、事件通知、信号通知、管道等等,下面为大家介绍一种本人在项目实践过常用并使用的一种线程通信方式,该通信方式主要仿消息队列实现一种类全局变量或线程变量的队列类。
单体类很多时候可以实现全局变量的效果,通过单体类+队列容器+线程锁可以快速构建线程之间的数据通道,达到安全通信的目的。
二、源码实现
下面给出该队列类QueueDataSingle的实现代码,是一个单体类模板,其继承父类QueueData,通过容器deque进行数据存储,并定义了线程锁PYMutex进行安全限制:
2.1 消息队列
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef _QUEUE_DATA_H_
#define _QUEUE_DATA_H_
/***********************************************************************
*Copyright 2020-03-06, pyfree
*
*File Name : queuedata.h
*File Mark :
*Summary :
*数据队列类,线程安全
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#include <queue>
#include <deque>
#include <stdio.h>
#include <string.h>
#include "Mutex.h"
template <class T>
class QueueData
{
public:
QueueData(std::string desc = "thread_queue");
~QueueData();
//
/**
* 获取队列大小
* @return {int } 队列大小
*/
int size();
/**
* 判定队列是否为空
* @return {bool } 是否为空队列
*/
bool isEmpty();
/**
* 获取队列头元素
* @param it {T&} 头元素
* @return {bool } 是否成功
*/
bool getFirst(T &it);
/**
* 删除元素
* @return {bool } 是否成功
*/
bool removeFirst();
/**
* 获取队列头元素,并从队列终删除
* @param it {T&} 头元素
* @return {bool } 是否成功
*/
bool pop(T &it);
/**
* 从队列头开始逐步获取多个元素,并剔除
* @param its {queue<T>&} 获取到的元素集
* @param sizel {int} 一次获取多少个
* @return {bool } 至少获取一个元素以上则成功
*/
bool getList(std::queue<T> &its,unsigned int sizel=5);
/**
* 从队列尾部添加元素
* @param it {T} 被添加元素
* @return {void } 无返回
*/
void add(T it);
/**
* 从队列头部添加元素
* @param it {T} 被添加元素
* @return {void } 无返回
*/
void add_front(T it);
/**
* 清空元素
* @return {void }
*/
void clear();
private:
void init();
QueueData& operator=(const QueueData&) {return this;};
protected:
std::string queue_desc;
private:
std::deque<T> datacache_queue; //队列容器
PYMutex m_Mutex; //线程锁,或者如果更彻底采用acl库,采用acl::thread_mutex替代
//
static unsigned int QSize; //队列大小约束,超出是会从队列头剔除旧数据腾出空位在对末添加数据
//
int queue_overS; //队列溢出次数计数
};
template <class T>
unsigned int QueueData<T>::QSize = 100;
template <class T>
QueueData<T>::QueueData(std::string desc)
: queue_desc(desc)
{
init();
};
template <class T>
void QueueData<T>::init()
{
queue_overS = 0;
};
template <class T>
QueueData<T>::~QueueData()
{
}
//
template <class T>
int QueueData<T>::size()
{
int ret = 0;
m_Mutex.Lock();
ret = static_cast<int>(datacache_queue.size());
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::isEmpty()
{
bool ret = false;
m_Mutex.Lock();
ret = datacache_queue.empty();
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::getFirst(T &it)
{
bool ret = false;
m_Mutex.Lock();
if (!datacache_queue.empty())
{
it = datacache_queue.front();
ret = true;
}
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::removeFirst()
{
bool ret = false;
m_Mutex.Lock();
if (!datacache_queue.empty())
{
datacache_queue.pop_front();
ret = true;
}
m_Mutex.Unlock();
return ret;
}
template <class T>
bool QueueData<T>::pop(T &it)
{
bool ret = false;
m_Mutex.Lock();
if (!datacache_queue.empty())
{
it = datacache_queue.front();
datacache_queue.pop_front();
ret = true;
}
m_Mutex.Unlock();
return ret;
};
template <class T>
bool QueueData<T>::getList(std::queue<T> &its,unsigned int sizel)
{
m_Mutex.Lock();
while (!datacache_queue.empty())
{
its.push(datacache_queue.front());
datacache_queue.pop_front();
if (its.size() >= sizel)
{
break;
}
}
m_Mutex.Unlock();
return !its.empty();
};
template <class T>
void QueueData<T>::add(T it)
{
m_Mutex.Lock();
if (datacache_queue.size() > QSize)
{
queue_overS++;
datacache_queue.pop_front();
}
datacache_queue.push_back(it);
m_Mutex.Unlock();
if (queue_overS >= 10)
{
//每溢出10次,报告一次
printf("add item to queue %s at end,but the size of QueueData is up to limmit size: %d .[%s %s %d]\n"
, queue_desc.c_str(), QSize
, __FILE__, __FUNCTION__, __LINE__);
queue_overS = 0;
}
}
template <class T>
void QueueData<T>::add_front(T it)
{
m_Mutex.Lock();
if (datacache_queue.size() > QSize)
{
queue_overS++;
datacache_queue.pop_front();
}
datacache_queue.push_front(it);
m_Mutex.Unlock();
if (queue_overS >= 10)
{
//每溢出10次,报告一次
printf("add item to queue %s at first,but the size of QueueData is up to limmit size: %d .[%s %s %d]\n"
, queue_desc.c_str(), QSize
, __FILE__, __FUNCTION__, __LINE__);
queue_overS = 0;
}
}
template <class T>
void QueueData<T>::clear()
{
m_Mutex.Lock();
datacache_queue.clear();
m_Mutex.Unlock();
queue_overS = 0;
}
#endif //_QUEUE_DATA_H_
其继承父类代码:
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef _QUEUE_DATA_SINGLE_H_
#define _QUEUE_DATA_SINGLE_H_
/***********************************************************************
*Copyright 2020-03-06, pyfree
*
*File Name : queuedata_single.h
*File Mark :
*Summary :
*单体类数据队列,线程安全,具体实现在其父类QueueData<T>
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#include "queuedata.h"
template <class T>
class QueueDataSingle : public QueueData<T>
{
public:
static QueueDataSingle* getInstance();
static void Destroy();
~QueueDataSingle();
void setQueueDesc(std::string desc);
private:
QueueDataSingle(std::string desc="single_thread_queue")
: QueueData<T>(desc)
{
};
QueueDataSingle& operator=(const QueueDataSingle&) {return this;};
private:
static QueueDataSingle* instance;
};
template <class T>
QueueDataSingle<T>* QueueDataSingle<T>::instance = NULL;
template <class T>
QueueDataSingle<T>* QueueDataSingle<T>::getInstance()
{
if (NULL == QueueDataSingle::instance)
{
QueueDataSingle::instance = new QueueDataSingle();
}
return QueueDataSingle::instance;
}
template <class T>
void QueueDataSingle<T>::Destroy()
{
if (NULL != QueueDataSingle::instance)
{
delete QueueDataSingle::instance;
QueueDataSingle::instance = NULL;
}
}
template <class T>
QueueDataSingle<T>::~QueueDataSingle()
{
}
template <class T>
void QueueDataSingle<T>::setQueueDesc(std::string desc)
{
this->queue_desc = desc;
}
#endif //_QUEUE_DATA_SINGLE_H_
2.2 线程锁
队列类依赖的线程锁实现代码如下:
Mutex.h
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef _PYMUTEX_H_
#define _PYMUTEX_H_
/***********************************************************************
*Copyright 2020-03-06, pyfree
*
*File Name : Mutex.h
*File Mark :
*Summary : 线程锁
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#ifdef WIN32
//#include <windows.h>
#else
#include <pthread.h>
#endif
typedef void *HANDLE;
class IMutex
{
public:
virtual ~IMutex() {}
/**
* 上锁
* @return {void}
*/
virtual void Lock() const = 0;
/**
* 尝试上锁
* @return {void}
*/
virtual bool TryLock() const = 0;
/**
* 解锁
* @return {void}
*/
virtual void Unlock() const = 0;
};
class PYMutex : public IMutex
{
public:
PYMutex();
~PYMutex();
virtual void Lock() const;
virtual bool TryLock() const;
virtual void Unlock() const;
private:
#ifdef _WIN32
HANDLE m_mutex;
#else
mutable pthread_mutex_t m_mutex;
#endif
};
#endif //_PYMUTEX_H_
Mutex.cpp
#include "Mutex.h"
#ifdef WIN32
#include <windows.h>
#endif
//#include <iostream>
#include <stdio.h>
PYMutex::PYMutex()
{
#ifdef _WIN32
m_mutex = ::CreateMutex(NULL, FALSE, NULL);
#else
pthread_mutex_init(&m_mutex, NULL);
#endif
}
PYMutex::~PYMutex()
{
#ifdef _WIN32
::CloseHandle(m_mutex);
#else
pthread_mutex_destroy(&m_mutex);
#endif
}
void PYMutex::Lock() const
{
#ifdef _WIN32
//DWORD d = WaitForSingleObject(m_mutex, INFINITE);
WaitForSingleObject(m_mutex, INFINITE);
/// \todo check 'd' for result
#else
pthread_mutex_lock(&m_mutex);
#endif
}
bool PYMutex::TryLock() const
{
#ifdef _WIN32
DWORD dwWaitResult = WaitForSingleObject(m_mutex, 0);
if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) {
printf("thread WARNING: bad result from try-locking mutex\n");
}
return (dwWaitResult == WAIT_OBJECT_0) ? true : false;
#else
return (0==pthread_mutex_trylock(&m_mutex))?true:false;
#endif
};
void PYMutex::Unlock() const
{
#ifdef _WIN32
::ReleaseMutex(m_mutex);
#else
pthread_mutex_unlock(&m_mutex);
#endif
}
三、 接口调用
定义了队列类后,下来我们看看其应用,以下为伪代码:
struct wd
{
int a;
float b;
};
thread01:
QueueDataSingle<wd> *queue_=QueueDataSingle<wd>::getInstance();
queue_->setQueueDesc("my_write_queue");
run:
wd it = //your code
queue_->add(it);
thread02:
QueueDataSingle<wd> *queue_=QueueDataSingle<wd>::getInstance();
wd it;
if(queue_->pop(it))
{
//your code
}
or:
if(queue_->getFirst(it))
{
//your code
//for some condition
if(!queue_->removeFirst())
{
//
}
}
由于队列类是单体类,甚至不必将其声明为线程类的变量,直接在某个需要使用的函数内直接即时声明即时使用,很便捷,另外其父类QueueData可以用在线程类某些场景下作为数据缓存使用,例如在sokcet通信读取时,优先将数据丢到缓存,业务解析处理根据线程资源另行处理,会给socket通信效率带来优化。