与shared_ptr Assertion px != 0 failed相似
我正在编写一个游戏服务器,它会生成一个新线程来处理每个用户会话 . 主线程有一个UserSession共享指针的std :: vector . 另一个线程定期从此向量中删除死会话但在执行std :: vector :: erase()时失败 . 我无法找出生活中的错误 .
错误是:
Prototype2:/usr/include/boost/smart_ptr/shared_ptr.hpp:653:typename boost :: detail :: sp_member_access :: type boost :: shared_ptr :: operator - >()const [with T = UserSession; typename boost :: detail :: sp_member_access :: type = UserSession *]:断言`px!= 0'失败 . 中止(核心倾倒)
相关代码是:
void GameServer::start()
{
int sessionid;
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port_));
boost::thread(&GameServer::session_monitor, this);
for (;;)
{
socket_shptr socket(new tcp::socket(io_service));
acceptor.accept(*socket);
sessionid = numsessions_++;
UserSession* usession = new
UserSession(socket, sessionid, io_service);
session_shptr session(usession);
sessions_mutex_.lock();
sessions_.push_back(session);
sessions_mutex_.unlock();
std::cout << "Starting session for client " <<
get_client_ip(*socket) << std::endl;
session->start();
}
}
void GameServer::session_monitor()
{
for (;;)
{
boost::this_thread::sleep(boost::posix_time::seconds(10));
std::cout << "Removing dead user sessions" << std::endl;
sessions_mutex_.lock();
for (std::vector<session_shptr>::iterator it = sessions_.begin();
it != sessions_.end(); ++it)
{
if ((*it)->is_dead())
{
std::cout << "Removing session: " << (*it)->id() <<
std::endl;
sessions_.erase(it);
}
}
sessions_mutex_.unlock();
}
}
2 回答
在迭代器上调用
erase
使其无效 . 然后,您尝试继续使用它来遍历列表 . 您需要使用erase
的返回值继续遍历列表 .从
std::vector
中删除内容的正确方法是使用删除 - 擦除习惯用法 . 循环一个容器并手动删除元素既烦人又不会更高效,而且它容易出错,因为erase
会使你的迭代器失效 .std::remove
和std::remove_if
已经是非常漂亮的实现了,我们可以通过调用erase
将它们捆绑起来,这样你编写的唯一代码就是不同擦除的代码 .这是这个成语的基于容器的版本:
现在您的代码如下所示:
或者,更短:
作为最后的问题,请注意,如果你的析构函数可以重入,你必须非常小心
std::vector
的状态 - 如果析构函数代码导致你正在改变的vector
,那你就麻烦了 .如果这是一个问题,您可以创建一个临时向量来将死进程填充到:
它将会话的破坏从锁定中移除(好!),并将其从代码中移出,这些代码遍历几乎全局可访问的
vector
(太棒了!) .这确实使用了一些C 11构造,但是如果你的编译器是纯C 03而且没有造成太大的破坏,那么大多数都可以被删除 . (去掉
forward
,用Container
替换&&
,用Lambda
和T
替换&&
) .您必须将lambda写为函数或函数对象,或者将
erase_remove_if
调用为bind
.