我有一台服务器可以根据要求将2名玩家聚集在一起并在一个新线程中开始游戏Game
。
struct GInfo {Game* game; std::thread* g_thread};
while (true) {
players_pair = matchPlayers();
Game* game = new Game(players_pair);
std::thread* game_T = new std::thread(&Game::start, game);
GInfo ginfo = {game, game_T}
_actives.push_back(ginfo); // std::list
}
我正在编写一个“垃圾收集器”,它运行在另一个线程中,以清除已终止游戏的内存。
void garbageCollector() {
while (true) {
for (std::list<Ginfo>::iterator it = _actives.begin(); it != _actives.end(); ++it) {
if (! it->game->isActive()) {
delete it->game; it->game = nullptr;
it->g_thread->join();
delete it->g_thread; it->g_thread = nullptr;
_actives.erase(it);
}
}
sleep(2);
}
}
这会产生一个段错误,我怀疑这是因为_active.erase(it)
处于迭代循环中。为了排除故障,我使_actives
成为std::vector
(而不是std::list
)并应用相同的算法,但使用索引而不是迭代器,它工作正常。
有没有解决的办法?
是算法,数据结构用得好吗?有没有更好的方法来进行垃圾收集?
感谢帮助!
如果您查看erase方法的文档,它会在删除元素后返回元素的迭代器。
使用它的方法是将返回值分配给迭代器,就像这样。
for (std::list<Ginfo>::iterator it = _actives.begin(); it != _actives.end();) {
if (! it->game->isActive()) {
delete it->game; it->game = nullptr;
it->g_thread->join();
delete it->g_thread; it->g_thread = nullptr;
it = _actives.erase(it);
}
else {
++it;
}
}
因为从erase
中获取返回值会将迭代器提升到下一个元素,所以我们必须确保在发生这种情况时不增加迭代器。
在一个不相关的注释中,以下划线开头的变量名通常保留给编译器的内部,应该在您自己的代码中避免。
有没有更好的方法来进行垃圾收集?
是的,不要一起使用new,delete
或动态内存:
struct Players{};
struct Game{
Game(Players&& players){}
};
struct GInfo {
GInfo(Players&& players_pair):
game(std::move(players_pair)),g_thread(&Game::start, game){}
Game game;
std::thread g_thread;
};
std::list<GInfo> _actives;
void someLoop()
{
while (true) {
GInfo& ginfo = _actives.emplace_back(matchPlayers());
}
}
void garbageCollector() {
while (true) {
//Since C++20
//_active.remove_if([](GInfo& i){ return !i.game.isActive();});
//Until C++20
auto IT =std::remove_if(_actives.begin(),_actives.end(),
[](GInfo& i){ return !i.game.isActive();});
_active.erase(IT,_active.end());
//
sleep(2);
}
}
可能会有一些拼写错误,但这就是主意。