请温柔一点——这是我第一次在这里发帖。目前正在自学C++。想玩线程并在它们之间传递任何类型的消息。还没有进入线程部分——只是处理消息队列元素。下面是我整理的代码,至少在一个main((线程中是有效的。在我开始创建自定义数据对象和线程函数之前,我想就我迄今为止所做的是否可行(如果没有完全符合惯例/标准或完全高效(达成共识,所以如果这会在以后的线程中产生不必要的复杂性,我真的只是在寻找方向。
所以有两个代码文件:
messageQueue.h,其中包括实现和标头,因为无法使链接器与单独的cpp文件中的实现一起工作。
#pragma once
#include<queue> // for queue
#include<thread> // for smart pointers
#include<mutex> // for locking of the queue when doing push(), front() and pop()
/*
Purpose:
To provide a thread safe method of of passing messages between threads in a FIFO queue manner.
Currently this uses unique_ptr which enables a one to one publisher/consumer application, if
we need one to many publisher/consumer model then will need to investigate maintaining a collection
of subscribers (consumers) and use shared_ptr.
Usage notes:
Message objects need to be created using make_unique not using new - e.g.
auto messagePtr = std::make_unique<message_t<std::string>>("this is a message");
Call this way:
messageQueue<objectType> messageQueueName;
messageQueueName.publish(std::move(messagePtr));
messagePtr = messageQueueName.consume();
*/
template<class T>
class message_t
{
public:
message_t(T message) : m_message{ message } {}
private:
T m_message;
};
template<class T>
class messageQueue
{
public:
messageQueue() {};
void publish(std::unique_ptr<message_t<T>> messagePtr);
std::unique_ptr<message_t<T>> consume();
bool hasData();
private:
std::queue < std::unique_ptr<message_t<T>>> m_queue;
std::mutex m_mutex;
};
// Had to add the implementation into the header file as templated classes have link issues when in separate cpp files :-(
template<class T>
void messageQueue<T>::publish(std::unique_ptr<message_t<T>> messagePtr) {
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.push(std::move(messagePtr));
};
template<class T>
std::unique_ptr<message_t<T>> messageQueue<T>::consume() {
std::unique_ptr<message_t<T>> retVal;
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_queue.empty()) { retVal = std::move(m_queue.front()); }
m_queue.pop();
}
return retVal;
};
template<class T>
bool messageQueue<T>::hasData() {
std::lock_guard<std::mutex> lock(m_mutex);
return(!m_queue.empty());
};
main.cpp-只有一个非常简单的测试用例。
#include "messageQueue.h"
#include<string>
#include<iostream>
int main(void) {
messageQueue<std::string> textQueue; // Create the message queue
auto messagePtr = std::make_unique<message_t<std::string>>("this is a message"); // Create a message and get its unique pointer
std::cout << "messagePtr= " << messagePtr << std::endl; // output the pointer value
textQueue.publish(std::move(messagePtr)); // Push the message pointer onto the queue : messagePtr is now 0
auto newMessagePtr = std::make_unique<message_t<std::string>>("this is another message"); // Create a second message and get its unique pointer
std::cout << "newMessagePtr= " << newMessagePtr << std::endl; // output the new message pointer value
std::cout << "Consuming messagePtr into newMessagePtr" << std::endl; //comment
if (textQueue.hasData()) { newMessagePtr = textQueue.consume(); } // Pull the original pointer off the queue and assign to new message pointer
std::cout << "newMessagePtr= " << newMessagePtr << std::endl; // output the pointer in the new message pointer - should be first pointer value
return(0);
}
我看到的一个问题是,如果消息队列为空,您仍然会从中弹出
还有一个概念性问题:在多线程环境中,hasData()
永远没有意义。无论它返回什么,都可能立即被另一个线程无效。因此,任何代码都不能真正依赖于返回值,除非你接受其正确性的不确定性。
更常见的方法是简单地无条件地try_consume
值(如果没有消息,则返回nullptr(——您基本上已经将其实现为consume
。
另一种方法是使用阻塞consume
,它会暂停当前线程,直到其他线程写入队列。然而,这需要使用信令,例如通过std::condition_variable
或原子。这有它的陷阱,但如果你正在学习,花时间了解它是如何工作的是值得的。