我有一个容器类Query_List
:
template <typename Data>
class query_list
{
private:
std::mutex mx_lock;
// Underlaying container for fast read, write and acces
std::list<Data> m_DataArray;
// Index table used for fast acces over the container
std::map<uint32_t, typename std::list<Data>::iterator> m_IndexTable;
public:
query_list() { }
void Push_Back(const uint32_t& ID, const Data& Val)
{
std::lock_guard<std::mutex> _l(mx_lock);
// Add data to the container
m_DataArray.push_back(Val);
// Get iterator to the new alocated data
auto iter = m_DataArray.end();
--iter;
// Asociate ID with the index in the list
m_IndexTable[ID] = iter;
}
bool AtID(const uint32_t& ID, Data &To_Get)
{
if (!Exists(ID))
return false;
std::lock_guard<std::mutex> _l(mx_lock);
To_Get = *m_IndexTable[ID];
return true;
}
void Remove(const uint32_t& ID)
{
// Data has already been freed!
if (!Exists(ID)) return;
std::lock_guard<std::mutex> _l(mx_lock);
m_DataArray.erase(m_IndexTable[ID]);
m_IndexTable[ID] = m_DataArray.end();
}
bool Exists(const uint32_t& ID)
{
std::lock_guard<std::mutex> _l(mx_lock);
if (m_IndexTable.find(ID) == m_IndexTable.end())
return false;
if (m_IndexTable[ID] == m_DataArray.end())
return false;
return true;
}
};
当我尝试从ID指向的容器中提取数据时出现问题:
bool PacketManager::AppendPacket(const Packet& pk)
{
PacketQueue _queue;
// The queue is passed by reference
if (!l_ConnQueues.AtID(pk.ownerID, _queue))
return false;
// Append the packet
std::lock_guard<std::mutex> _l(_queue._mx);
size_t InitSize = _queue.OutPackets.size();
_queue.OutPackets.push(pk);
// If data is not appended to the queue
if (_queue.OutPackets.size() <= InitSize)
return false;
return true;
}
调试该函数向我显示数据从队列中附加到临时对象,但不是从容器中附加到临时对象。我怀疑这种行为的原因是PackeTQueue
类的 复制构造函数 。
struct PacketQueue
{
PacketQueue() { }
uint32_t ID;
std::mutex _mx;
std::queue<Packet> OutPackets;
PacketQueue& operator=(const PacketQueue& q)
{
ID = q.ID;
OutPackets = q.OutPackets;
return *this;
}
PacketQueue(const PacketQueue& queue)
{
ID = queue.ID;
OutPackets = queue.OutPackets;
}
};
我的问题是:
Query_List
)?答案 0 :(得分:0)
问题是,您的AtID()
方法正在返回PacketQueue
中存储的m_DataArray
的副本。如果要访问原始文件以便修改它,则需要更改To_Get
输出参数以返回指针:
bool AtID(uint32_t ID, Data* &To_Get)
{
std::lock_guard<std::mutex> l(mx_lock);
auto iter = m_IndexTable.find(ID);
if (iter == m_IndexTable.end())
return false;
To_Get = &*(iter->second);
return true;
}
bool PacketManager::AppendPacket(const Packet& pk)
{
PacketQueue *queue;
if (!l_ConnQueues.AtID(pk.ownerID, queue))
return false;
std::lock_guard<std::mutex> l(queue->_mx);
size_t InitSize = queue->OutPackets.size();
queue->OutPackets.push(pk);
return (queue->OutPackets.size() > InitSize);
}
或者,您可以更改AtID()
以将指针作为返回值返回,而不是使用输出参数:
Data* AtID(uint32_t ID)
{
std::lock_guard<std::mutex> l(mx_lock);
auto iter = m_IndexTable.find(ID);
if (iter == m_IndexTable.end())
return nullptr;
return &*(iter->second);
}
bool PacketManager::AppendPacket(const Packet& pk)
{
PacketQueue *queue = l_ConnQueues.AtID(pk.ownerID);
if (!queue)
return false;
std::lock_guard<std::mutex> l(queue->_mx);
queue->OutPackets.push(pk);
return true;
}
当然,话虽如此,由于l_ConnQueues
的互斥锁在AtID()
退出后解锁,任何其他线程都可以潜在地 Remove()
列表PacketQueue
,而AppendPacket()
仍在尝试将数据包推入其中。因此,让AppendPacket()
保持列表的互斥锁被锁定直到更新返回的队列为止会更安全:
Data* AtID_NoLock(uint32_t ID)
{
auto iter = m_IndexTable.find(ID);
if (iter == m_IndexTable.end())
return nullptr;
return &*(iter->second);
}
Data* AtID(uint32_t ID)
{
std::lock_guard<std::mutex> l(mx_lock);
return AtID_NoLock(ID);
}
bool PacketManager::AppendPacket(const Packet& pk)
{
std::lock_guard<std::mutex> l(l_ConnQueues.mx_lock);
PacketQueue *queue = l_ConnQueues.AtID_NoLock(pk.ownerID);
if (!queue)
return false;
std::lock_guard<std::mutex> l2(queue->_mx);
queue->OutPackets.push(pk);
return true;
}
话虽如此,您会注意到我已将AtID()
更改为不再使用Exists()
。存在竞争条件(也在Remove()
中),只要Exists()
退出,另一个线程就可以进入并锁定互斥锁并更改list
/ map
在当前线程有机会重新锁定互斥锁之前。因此,AtID()
(和Remove()
)根本不应该调用Exists()
。
此外,我建议Remove()
将std:list
存储的end
迭代器设置为AtID()
,这样可以为erase
做更多的工作。最好只从ID
完全map
找到void Remove(uint32_t ID)
{
std::lock_guard<std::mutex> l(mx_lock);
auto iter = m_IndexTable.find(ID);
if (iter != m_IndexTable.end())
{
m_DataArray.erase(iter->second);
m_IndexTable.erase(iter);
}
}
:
std::map
更新:话虽如此,根本没有充分理由拥有std::list
个PacketQueue
迭代器。您可以将std::map
个对象直接存储在std::list
中,然后完全删除template <typename Data>
class query_list {
private:
std::mutex mx_lock;
std::map<uint32_t, Data> m_Data;
public:
query_list() { }
void Push_Back(uint32_t ID, const Data& Val) {
std::lock_guard<std::mutex> l(mx_lock);
// Add data to the container
m_Data[ID] = Val;
}
Data* AtID_NoLock(uint32_t ID) {
auto iter = m_Data.find(ID);
return (iter != m_Data.end()) ? &(iter->second) : nullptr;
}
Data* AtID(uint32_t ID) {
std::lock_guard<std::mutex> l(mx_lock);
return AtID_NoLock(ID);
}
void Remove(uint32_t ID) {
std::lock_guard<std::mutex> l(mx_lock);
auto iter = m_Data.find(ID);
if (iter != m_Data.end())
m_Data.erase(iter);
}
bool Exists(uint32_t ID) {
std::lock_guard<std::mutex> l(mx_lock);
return (m_Data.find(ID) != m_Data.end());
}
};
:
t.test(Diarrhea ~ Max_by_90_H2SPos_12, data = noch4, paired=T)