类似shared_ptr断言px!=0失败
我正在编写一个游戏服务器,它产生一个新线程来处理每个用户会话。主线程有一个UserSession共享指针的std::向量。另一个线程定期从这个向量中删除死会话,但在执行std::向量::erase()时失败了。我找不到我的生活有什么问题。
错误是:
原型2: /usr/include/boost/smart_ptr/shared_ptr.hpp:653:typename boost::de尾部::sp_member_access::类型boost::shared_ptr::操作符-
相关代码为:
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();
}
}
在迭代器上调用erase
会使其无效。然后,您尝试继续使用它来遍历列表。您需要使用erase
的返回值来继续遍历列表。
for (std::vector<session_shptr>::iterator it = sessions_.begin(); it != sessions_.end(); )
{
if ((*it)->is_dead())
{
std::cout << "Removing session: " << (*it)->id() <<
std::endl;
it = sessions_.erase(it);
}
else
++it;
}
从std::向量
中删除内容的正确方法是使用移除-擦除习惯用法。循环遍历容器并手动删除元素既烦人又不会更有效,而且容易出错,因为擦除
会使迭代器无效。
std::删除
和std::remove_if
已经是非常巧妙的实现,我们可以通过调用erase
将它们捆绑在一起,这样您编写的唯一代码就是不同于不同擦除的代码。
以下是这个习惯用法的基于容器的版本:
template<typename Container, typename Lambda>
Container&& remove_erase_if( Container&& c, Lambda&& test ) {
using std::begin; using std::end;
auto it = std::remove_if( begin(c), end(c), std::forward<Lambda>(test) );
c.erase(it, c.end());
return std::forward<Container>(c);
}
template<typename Container, typename T>
Container&& remove_erase( Container&& c, T&& test ) {
using std::begin; using std::end;
auto it = std::remove( begin(c), end(c), std::forward<T>(test) );
c.erase(it, c.end());
return std::forward<Container>(c);
}
现在你的代码是这样的:
sessions_mutex_.lock();
remove_erase_if( sessions_, []( session_shptr& ptr )->bool {
if (ptr->is_dead()) {
std::cout << "Removing session: " << ptr->id() << std::endl;
ptr.reset(); // optional
return true;
} else {
return false;
}
});
sessions_mutex_.unlock();
或者,更短:
sessions_mutex_.lock();
remove_erase_if( sessions_, []( session_shptr& ptr ) {
return ptr->is_dead();
});
sessions_mutex_.unlock();
作为最后一个问题,请注意,如果您的析构函数可以重入,您必须非常小心您的std::向量
的状态-如果析构函数代码导致您正在处理的向量
被更改,您就有麻烦了。
如果这是一个问题,您可以创建一个临时向量来将死进程填充到:
std::vector<session_shptr> tmp;
sessions_mutex_.lock();
remove_erase_if( sessions_, []( session_shptr& ptr ) {
if (!ptr->is_dead())
return false;
tmp.emplace_back( std::move(ptr) );
return true;
});
sessions_mutex_.unlock();
tmp.clear();
它将会话的销毁移出锁(好!),并将其移出迭代几乎全局可访问的向量
的代码(太好了!)。
这确实使用了一些C 11结构,但如果您的编译器是纯C 03,则可以去除大部分结构而不会造成太大损坏。(去掉前进
s,替换
您必须将lambda写成函数或函数对象,或者作为erase_remove_if
调用的绑定
。