C++ pair类型的泛型容器的线程安全实现<;无符号整数,boost::any>;使用共享PTR
我创建了一个通用消息队列,用于多线程应用程序。具体而言,单一生产商,多消费者。主要代码如下 1) 我想知道是应该通过值将分配了new的共享\u ptr传递到enqueue方法中,还是让队列包装器分配内存本身并通过const引用传入genericsg对象更好 2) 我应该让我的出列方法返回一个shared_ptr,通过引用将shared_ptr作为参数传入(当前策略),还是让它直接返回一个genericsg对象 3) 我是否需要信号/等待进入/退出队列,或者读/写锁是否足够 4) 我甚至需要使用共享的PTR吗?或者这仅仅取决于我使用的实现?我喜欢一旦所有引用不再使用对象,共享的ptr就会释放内存。不过,如果推荐的话,我可以轻松地将其移植到常规指针 5) 我在这里存储了一对,因为我想区分我处理的是什么类型的消息,而不必进行任何类型的转换。每个消息类型都有一个引用特定结构的唯一ID。有更好的方法吗 通用消息类型:C++ pair类型的泛型容器的线程安全实现<;无符号整数,boost::any>;使用共享PTR,c++,multithreading,boost,stl,C++,Multithreading,Boost,Stl,我创建了一个通用消息队列,用于多线程应用程序。具体而言,单一生产商,多消费者。主要代码如下 1) 我想知道是应该通过值将分配了new的共享\u ptr传递到enqueue方法中,还是让队列包装器分配内存本身并通过const引用传入genericsg对象更好 2) 我应该让我的出列方法返回一个shared_ptr,通过引用将shared_ptr作为参数传入(当前策略),还是让它直接返回一个genericsg对象 3) 我是否需要信号/等待进入/退出队列,或者读/写锁是否足够 4) 我甚至需要使用共
template<typename Message_T>
class genericMsg
{
public:
genericMsg()
{
id = 0;
size = 0;
}
genericMsg (unsigned int &_id, unsigned int &_size, Message_T &_data)
{
id = _id;
size = _size;
data = _data;
}
~genericMsg()
{}
unisgned int id;
unsigned int size;
Message_T data; //All structs stored here contain only POD types
};
数据类型:
std::deque < std::pair< unsigned int, boost::any> > qData;
生产者代码片段:
boost::shared_ptr<genericMessage> tmp (new derived_msg1(MSG1_ID));
theQueue.enqueue(tmp);
boost::shared_ptr<genericMessage> tmp = theQueue.dequeue();
if(tmp->getID() == MSG1_ID)
{
boost::shared_ptr<derived_msg1> tObj = boost::dynamic_pointer_cast<derived_msg1>(tmp);
tObj->printData();
}
boost::shared_ptr tmp(新派生的_msg1(msg1_ID));
排队(tmp);
消费者信息片段:
boost::shared_ptr<genericMessage> tmp (new derived_msg1(MSG1_ID));
theQueue.enqueue(tmp);
boost::shared_ptr<genericMessage> tmp = theQueue.dequeue();
if(tmp->getID() == MSG1_ID)
{
boost::shared_ptr<derived_msg1> tObj = boost::dynamic_pointer_cast<derived_msg1>(tmp);
tObj->printData();
}
boost::shared_ptr tmp=theQueue.dequeue();
如果(tmp->getID()==MSG1\u ID)
{
boost::shared\u ptr tObj=boost::dynamic\u pointer\u cast(tmp);
tObj->printData();
}
新队列:
std::deque< boost::shared_ptr<genericMessage> > qData;
std::dequeqData;
新排队:
void mq_class::enqueue(const boost::shared_ptr<genericMessage> &data_in)
{
boost::unique_lock<boost::mutex> lock(mut);
this->qData.push_back(data_in);
cond.notify_one();
}
void mq_类::排队(const boost::共享_ptr和数据_in)
{
boost::唯一锁定(mut);
此->qData.push_back(数据输入);
第二,通知某人;
}
新出列:
boost::shared_ptr<genericMessage> mq_class::dequeue()
{
boost::shared_ptr<genericMessage> ptr;
{
boost::unique_lock<boost::mutex> lock(mut);
while(qData.empty())
{
cond.wait(lock);
}
ptr = qData.front();
qData.pop_front();
}
return ptr;
}
boost::shared_ptr mq_class::dequeue()
{
boost::共享的ptr;
{
boost::唯一锁定(mut);
while(qData.empty())
{
等待(锁定);
}
ptr=qData.front();
qData.pop_front();
}
返回ptr;
}
现在,我的问题是我的出列是否正确?还有别的方法吗?在这种情况下,我是否应该传入一个共享的\u ptr作为引用来实现我想要的功能?如果我想要线程安全,我通常使用const对象,并且仅在复制或创建构造函数时进行修改。这样,您就不需要使用任何锁定机制。在线程系统中,它通常比在单个实例上使用
mutex
es更有效
在您的情况下,只有deque
需要锁定。编辑(我为第1、2和4部分添加了答案)
1) 您应该有一个工厂方法来创建新的GenericMsg并返回一个std::unique\u ptr
。绝对没有理由通过const引用传入genericsg,然后让队列将其包装在智能指针中:一旦通过引用传入,您就失去了所有权的踪迹,因此如果这样做,队列将不得不构造(通过复制)整个genericsg来包装
2) 我想不出在什么情况下引用共享\u ptr
或唯一\u ptr
或自动\u ptr
是安全的。shared_ptr和unique_ptr用于跟踪所有权,一旦您对它们(或它们的地址)进行了引用,您就不知道还有多少引用或指针仍然存在,希望shared_ptr/unique_ptr对象包含有效的裸指针
unique_ptr始终优于裸指针,在一次只有一段代码(有效)指向对象的情况下,unique_ptr优于shared_ptr
- (答案解释了为什么这是一个好的实践,而不是一个坏的实践)
dequeue
函数中使用std::condition\u变量。在调用qData.front()
或qData.pop\u front()
之前,需要测试qData
是否为空。如果qData
为空,则需要等待条件变量。当enqueue
插入一个项目时,它应该向条件变量发出信号,以唤醒可能一直在等待的任何人
您对读写器锁的使用完全不正确。不要使用读写器锁。使用std::mutex
。读卡器锁只能用于完全为const
的方法。您正在dequeue
中修改qData
,因此读卡器锁将导致那里的数据争用。(读写器锁仅在您的愚蠢代码同时为常量并在较长时间内保持锁的情况下适用。您仅在插入或从队列中移除所需的时间内保持锁,因此即使您是const
,读写器锁的额外开销也将是净损失。)
可以在以下位置找到使用互斥量和条件变量实现(有界)缓冲区的示例:
4) unique\u ptr
总是比裸指针更受欢迎,并且通常比shared\u ptr
更受欢迎。(shared_ptr可能更好的主要例外是图形数据结构。)在像您这样的情况下,您正在一边读取内容,用工厂创建一个新对象,将所有权移动到队列,然后将所有权从队列移动到消费者,听起来您应该使用unique_ptr
5) 你在重新创造。虚拟函数被添加到C++中,这样你就不需要这样做了。您应该从一个名为do_it()
(或者更好的是,operator()()
之类的虚拟函数)的类中对消息进行子类化。然后将每个结构作为消息类的子类,而不是标记每个结构。当您将每个struc出列时
std::deque< boost::shared_ptr<genericMessage> > qData;
void mq_class::enqueue(const boost::shared_ptr<genericMessage> &data_in)
{
boost::unique_lock<boost::mutex> lock(mut);
this->qData.push_back(data_in);
cond.notify_one();
}
boost::shared_ptr<genericMessage> mq_class::dequeue()
{
boost::shared_ptr<genericMessage> ptr;
{
boost::unique_lock<boost::mutex> lock(mut);
while(qData.empty())
{
cond.wait(lock);
}
ptr = qData.front();
qData.pop_front();
}
return ptr;
}