前言
本文提供了一种C++消息队列的实现方式(包括一个线程类和一个消息队列类),通过一个简单示例说明如何生产消息和消费消息。
一、类图
消息队列类
#pragma once
#include <mutex>
#include <condition_variable>
#include <queue>
namespace wxw
{
template <typename Msg>
class WMessageQueue
{
private:
std::mutex m_msg_mutex;
std::condition_variable m_cond;
std::queue<std::shared_ptr<Msg>> m_queue;
bool m_is_running = true;
public:
std::shared_ptr<Msg> receiveMsg()
{
std::unique_lock<std::mutex> lg(m_msg_mutex);
m_cond.wait(lg, [this]()
{ return (!m_queue.empty() || !m_is_running); });
if (m_queue.empty())
{
return nullptr;
}
auto msg = m_queue.front();
m_queue.pop();
return msg;
}
void sendMessage(std::shared_ptr<Msg> const &msg)
{
std::unique_lock<std::mutex> lg(m_msg_mutex);
m_queue.emplace(msg);
m_cond.notify_one();
}
void stop()
{
m_is_running = false;
m_cond.notify_one();
}
};
}
调用std::condition_variable
的wait
方法前需要获取锁,wait
会释放锁,并阻塞当前线程,直到wait
的第二个参数返回true
(某种条件满足),wait
会重新上锁并执行下面的代码。
线程类
#pragma once
#include <thread>
#include "wang_message_queue.h"
namespace wxw
{
template <typename Msg>
class WThread
{
private:
bool m_is_running;
std::thread m_thread;
WMessageQueue<Msg> m_message_queue;
void run()
{
while (m_is_running)
{
auto msg = m_message_queue.receiveMsg();
if (msg != nullptr)
{
onMessage(msg);
}
}
}
void setThreadName(std::string const name)
{
pthread_setname_np(m_thread.native_handle(), name.c_str());
}
public:
WThread() : m_is_running(true)
{
m_thread = std::thread(&WThread::run, this);
}
WThread(std::string const name) : m_is_running(true)
{
m_thread = std::thread(&WThread::run, this);
setThreadName(name);
}
virtual ~WThread() = default;
virtual void onMessage(std::shared_ptr<Msg> &msg) = 0;
void sendMessage(std::shared_ptr<Msg> const &msg)
{
m_message_queue.sendMessage(msg);
}
void stop()
{
m_message_queue.stop();
if (m_is_running)
{
m_is_running = false;
m_thread.join();
}
}
};
}
二、使用步骤
1.定义自己的消息体对象
消息体可以定义成抽象类,然后被各种消息类继承,本文只定义一个具体的消息类
//.h
#pragma once
namespace wxw
{
class WorkMessage
{
private:
/* data */
int m_num;
int m_id;
public:
WorkMessage(int num, int id);
~WorkMessage();
void handleMessage();
};
}
//.cpp
#include "work_message.h"
#include <iostream>
#include <unistd.h>
namespace wxw
{
WorkMessage::WorkMessage(int num, int id)
: m_num(num), m_id(gettid())
{
}
WorkMessage::~WorkMessage()
{
}
void WorkMessage::handleMessage()
{
std::cout << "num:" << m_num << ",id:" << m_id << std::endl;
}
}
2.先定义自己的工作线程类
继承上述的类WThread
//.h
#pragma once
#include "wang_thread.h"
#include "work_message.h"
namespace wxw
{
class WorkThread final : public WThread<WorkMessage>
{
private:
/* data */
WorkThread(std::string const thread_name = "work_thread");
public:
~WorkThread() = default;
static WorkThread &getInstance()
{
static WorkThread work_thread_instance;
return work_thread_instance;
}
void onMessage(std::shared_ptr<WorkMessage> &msg) final;
};
}
//.cpp
#include "work_thread.h"
namespace wxw
{
WorkThread::WorkThread(std::string const thread_name)
: WThread(thread_name)
{
}
void WorkThread::onMessage(std::shared_ptr<WorkMessage> &msg)
{
msg->handleMessage();
}
}
3.测试结果
3.1 单线程访问
#include "work_thread.h"
#include <unistd.h>
int main(int argc, char *argv[])
{
int num = 0;
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
num++;
wxw::WorkThread::getInstance().sendMessage(std::make_shared<wxw::WorkMessage>(num, gettid()));
if (num == 10)
break;
}
wxw::WorkThread::getInstance().stop();
return 0;
}
测试结果
3.2 多线程访问
#include "work_thread.h"
#include <unistd.h>
#include <iostream>
void work_func()
{
int num = 0;
while (true)
{
// std::this_thread::sleep_for(std::chrono::milliseconds(1000));
num++;
wxw::WorkThread::getInstance().sendMessage(std::make_shared<wxw::WorkMessage>(num, gettid()));
if (num == 10)
break;
}
std::cout << "work func exit, id:" << gettid() << std::endl;
}
int main(int argc, char *argv[])
{
std::thread t1(work_func);
std::thread t2(work_func);
t1.join();
t2.join();
wxw::WorkThread::getInstance().stop();
return 0;
}
测试结果