2013-07-22 132 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