2013-07-22 131 views
2

类似于shared_ptr Assertion px != 0 failedstd :: vector :: erase()(多线程)'assertion'px!= 0'失败。'

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

的错误是:

Prototype2:/usr/include/boost/smart_ptr/shared_ptr.hpp:653:类型名称的boost ::详细:: sp_member_access ::类型 的boost :: shared_ptr的::运算符 - >()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(); 
    } 
} 
+0

我也意识到这个代码还有其他的错误,比如std :: cout的并发使用。 – coffeebean

+0

无关:总是使用'make_shared'。使用'std :: lock_guard'而不是直接锁定。 – Casey

回答

5

上进行迭代的迭代调用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; 
    } 
+0

这是正确的(哎呀)。谢谢! – coffeebean

2

std::vector删除的事情以正确的方式是使用remove擦除成语。遍历容器并手动删除元素既烦人又不会更高效,而且容易出错,因为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