我正在编写一个多线程的c ++ WIN套接字服务器,我遇到了一些奇怪的问题。
我正在使用向量来存储活动连接。我使用win mutex锁定向量然后我尝试迭代它以查找所有已关闭的连接并删除它们然后释放互斥锁。
代码:
if (!m_activeConnections.empty()){
for(std::vector<Connection*>::iterator it = m_activeConnections.begin(); it != m_activeConnections.end(); ++it) {
if ((*it)->isClosed()){
delete *it;
it = m_activeConnections.erase(it);
break;
}
}
cout << "\n \t Active Connections: " << m_activeConnections.size() << endl;
}
它的工作方式与此类似,但是当我删除break
行时,它总是再循环一次,迭代器it
指向0Xaaaaa并抛出异常。如果在创建新Connections的同一线程中完成此删除,即使没有中断也可以正常工作。这是为什么?
答案 0 :(得分:1)
每当修改范围时,必须确保更新用于遍历范围的迭代器,其方式与从范围擦除时迭代器无效的方式兼容。
将此方法应用于向量的一个简单示例是以下循环。请注意,擦除会从擦除点开始使所有迭代器无效,因此在擦除时需要获取新的迭代器,并且每次还需要重新计算end()
(即不要将结束计算提升出来)循环):
for (auto it = m_activeConnections.begin(); it != m_activeConnections.end(); )
{
if ((*it)->isClosed()) { it = m_activeConnections.erase(it); }
else { ++it; }
}
从矢量中删除的更好方法是将待删除元素移动到矢量背面,然后一次性擦除整个范围,避免一直移动尾部范围。通常我们使用remove_if
执行此操作,但您需要添加一些技巧来删除您案例中的指针:
m_activeConnections.erase(
std::remove_if(m_activeConnections.begin(),
m_activeConnections.end(),
[](Connection * p) {
if (p->isClosed()) { delete p; return true; }
return false;}),
m_activeConnections.end());
如果将容器更改为std::vector<std::unique_ptr<Connection>>
,则可以避免这种欺骗:每个职责都有一个类(向量包含,唯一指针删除),算法可以组合。
如果您不能通过选择合适的抽象来简化您的代码,您还可以尝试更复杂的算法:首先根据删除的需要对范围进行分区,然后删除,然后删除范围:
auto it = std::stable_partition(m_activeConnections.begin(),
m_activeConnections.end(),
[](Connection * p) { return p->isClosed(); });
for (auto kt = it; kt != m_activeConnections.end(); ++kt)
{
delete *kt;
}
m_activeConnections.erase(it, m_activeConnections.end());