std::vector::erase() (多线程) '断言 'px != 0' 失败。

std::vector::erase() (multithreaded) 'Assertion `px != 0' failed.'

本文关键字:失败 断言 多线程 erase std vector px      更新时间:2023-10-16

类似于 shared_ptr 断言 px != 0 失败

我正在编写一个游戏服务器,它会生成一个新线程来处理每个用户会话。主线程有一个 std::vector 的 UserSession 共享指针。另一个线程定期从此向量中删除死会话,但在执行 std::vector::erase(( 时失败。我无法找出我的生活出了什么问题。

错误是:

原型 2:/usr/include/boost/

smart_ptr/shared_ptr.hpp:653: typename boost::d etail::sp_member_access::type boost::shared_ptr::operator->(( const [with T = UserSession; typename boost::d etail::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();
    }
}

在迭代器上调用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::vector中删除内容的正确方法是使用删除-擦除习惯用法。 循环访问容器并手动删除元素既烦人又不会更有效率,而且很容易出错,因为erase会使您的迭代器失效。

std::removestd::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::vector的状态 - 如果析构函数代码导致你正在处理的vector被更改,你就有麻烦了。

如果这是一个问题,您可以创建一个临时向量来填充死进程:

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();

它将会话的销毁移出锁(好!(,并将其移出在几乎全局可访问vector上迭代的代码(太好了!(。

这确实使用了一些 C++11 构造,但如果编译器是纯 C++03 而不会造成太大损害,则可以删除大多数构造。 (去掉forward s,将&&替换为Container上的&,并将&&替换为LambdaT上的const &(。

您必须将 lambda 编写为函数或函数对象或erase_remove_if调用的bind