2014-11-03 230 views
0

我试图找到段错误背后的罪魁祸首。我的调试器告诉我,这个错误所在的变量没有数据。每10秒钟,我的C++代码中会有一个脚本运行。它做了“垃圾收集”并删除了一些可能死亡的“会话”。 为了有效地执行此操作,我使用时间戳 - 上次访问数据的时间。如果数据超过10秒钟,它就已经死了。在客户端上每隔4秒触发一次keepalive命令。std :: unordered_map中可能存在的错误

要执行这个GC,我循环访问一个std :: unordered_map,然后减去从epoch开始的当前时间。如果时间太长,我将它添加到一个std :: vector中,它保存要删除的键(是的,我知道它可以优化来跳过这一步)。

我面临的问题是它第一次正确循环。然而,此后,我得到一个segfault,它指向的迭代器值大于地图的大小。 只需切换回标准的std :: map就可以修复整个问题!

我将附加完成所有这些功能。所有的代码在http://github.com/yash101/DrawingPad

现已上市,代码[{} sourcedir /source/Session.cxx]:

void SessionHost::cron() 
{ 
    while(true) 
    { 
     std::this_thread::sleep_for(std::chrono::seconds(10)); 
     if(DEBUG) 
     { 
      std::cout << "Cron has started!" << std::endl; 
     } 

     while(!locky_thingy.try_lock_for(std::chrono::milliseconds(MUTEX_TIMEOUT))) 
     {} 

     int timethrough = 0; 
     std::vector<std::string> del; 
     for(std::map<std::string, long>::iterator ite = timestamp.begin(); ite != timestamp.end(); ++ite) 
     { 
      timethrough++; 
      std::cout << "Time through: " << timethrough << std::endl; 
      std::string curkey = ite->first; 
      long curval = ite->second; 
      std::cout << "Key: " << curkey << std::endl; 
      if(DEBUG) 
      { 
       std::cout << "Checking " << curkey << " with old ts of " << curval << std::endl; 
      } 

      u_int64_t curtm = std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now().time_since_epoch()).count(); 
      if(DEBUG) 
      { 
       std::cout << "Current time: " << curtm << std::endl; 
      } 
      if(curtm - curval > SESSION_TIMEOUT) 
      { 
       if(DEBUG) 
       { 
        std::cout << "Deleted session handle: [" << curkey << "]" << std::endl; 
       } 
       del.push_back(curkey); 
      } 
      else 
      { 
       if(DEBUG) 
       { 
        std::cout << "Kept back session handle: [" << curkey << "]" << std::endl; 
       } 
      } 

      for(unsigned int i = 0; i < del.size(); i++) 
      { 
       timestamp.erase(del[i]); 
       data.erase(del[i]); 
       std::cout << "Erasing: " << del[i] << std::endl; 
      } 
     } 

     locky_thingy.unlock(); 
    } 
} 
+0

std :: map timestamp <==在{sourcedir} /include/Session.hxx – yash101 2014-11-03 01:57:08

+1

我在这段代码中看不到'unordered_map' – Barry 2014-11-03 02:00:00

+1

你在做什么并行?确保在更新地图时没有数据竞争,因为这会使迭代器无效,因为您共享数据。否则,使用'std :: begin'和'std :: end'的'std :: unordered_map'应该总是有效。 – vsoftco 2014-11-03 02:03:43

回答

7

您有:

for(std::map<std::string, long>::iterator ite = timestamp.begin(); 
            ite != timestamp.end(); ++ite) 
{ 
    // ... 
    for(unsigned int i = 0; i < del.size(); i++) 
    { 
     timestamp.erase(del[i]); // <-- 
     // ... 
    } 
} 

unordered_map,擦除可以使迭代器无效。所以当你遍历时你不能抹去 - 试着想出一个不同的算法。 (我假设你的问题的某个版本涉及timestamp是一个unordered_map - 虽然在代码中没有提及这种类型)。

+1

他正在使用键删除,所以应该是好的? – Surt 2014-11-03 02:04:41

+2

@Surt如果'ite'当前是已删除键的迭代器,则不会。“被擦除元素的引用和迭代器失效,其他迭代器和引用不会失效。” – cdhowie 2014-11-03 02:05:23

+0

我切换到矢量,因为我第一次加载所有要删除的东西。然后我在迭代器完成后将它们从映射中删除 – yash101 2014-11-03 02:09:19

1

我认为错误是在这里

 for(unsigned int i = 0; i < del.size(); i++) 
     { 
      timestamp.erase(del[i]); 
      data.erase(del[i]); 
      std::cout << "Erasing: " << del[i] << std::endl; 
     } 
} // <---------------- this is the end of the iterator loop 

应该上升前的for循环,因此不会失效。

} // <---------------- this is the end of the iterator loop 

for(unsigned int i = 0; i < del.size(); i++) 
     { 
     timestamp.erase(del[i]); 
     data.erase(del[i]); 
     std::cout << "Erasing: " << del[i] << std::endl; 
} 

data.erase如果是向量,也可能有错。

如果您有需要擦除你应该标记记录,并使用

data.erase(std::remove_if(data.begin(), data.(end), CheckMark)); 

的remove_if将所有有效数据,数据的开始,擦除然后从后的最后一个有效清除的向量。

+0

嗯。这应该是在外面。但是,在使用矢量之前发生了同样的错误。我使用std :: map修正了错误,而不是std :: unordered_map – yash101 2014-11-03 02:13:11

+1

擦除的行为不同取决于所调用的容器,坏设计? – Surt 2014-11-03 02:15:04

+0

我猜。即使我在迭代时删除,但我更改为常规地图后,它的工作原理也很奇怪! – yash101 2014-11-03 02:17:56