嵌套循环中对mongocxx游标使用auto范围for触发SIGSEGV问题咨询
错误原因及不当操作说明
mongocxx::cursor是单次遍历的前向迭代器类型,底层封装的是MongoDB服务端返回的一次性结果流,不支持回退、不支持重复遍历,也不支持同时用多个迭代器遍历同一个cursor实例,触发段错误的具体逻辑如下:
- 外层循环第一次迭代时,cursor指向第一个查询结果,你拿到的
doc是不持有数据的bson文档视图,数据生命周期由cursor管理 - 进入内层循环后,继续用同一个cursor迭代,会直接遍历完剩余的所有结果,直到cursor走到尾后空位置,迭代过程中每取下一个元素,前一个元素对应的内存空间就会被cursor释放
- 内层循环结束回到外层时,cursor已经为空,且外层第一次拿到的
doc对应内存已经被释放,此时不管是外层尝试取下一个元素,还是内层循环中访问已经失效的doc,都会访问野指针,触发SIGSEGV段错误
修复方案
如果需要对查询结果做笛卡尔积嵌套遍历,可根据场景选择以下两种方案:
方案1:小结果集预读取到内存(推荐)
先将所有查询结果转成持有数据的bson对象存入容器,再嵌套遍历容器,性能更高:
// 预读取所有结果到vector,document::value会持有完整的bson数据,生命周期和vector绑定 std::vector<bsoncxx::document::value> result_set; mongocxx::cursor cursor = coll.find({}); for (auto&& doc_view : cursor) { result_set.emplace_back(doc_view); } // 嵌套遍历本地内存中的结果集即可 for (auto&& doc : result_set) { std::cout << "TEST FIRST" << bsoncxx::to_json(doc.view()) << "\n"; for (auto&& doc2 : result_set) { std::cout << "FIRST: " << bsoncxx::to_json(doc.view()) << "\n"; std::cout << "SECOND: " << bsoncxx::to_json(doc2.view()) << "\n"; } }
方案2:大结果集不占内存的实现
如果结果集太大无法全部存入内存,每次内层循环都重新发起查询生成独立的cursor即可:
mongocxx::cursor outer_cursor = coll.find({}); for(auto doc : outer_cursor) { std::cout << "TEST FIRST" << bsoncxx::to_json(doc) << "\n"; // 内层循环独立发起查询,生成新的cursor,和外层cursor互不影响 mongocxx::cursor inner_cursor = coll.find({}); for (auto doc2 : inner_cursor) { std::cout << "FIRST: " << bsoncxx::to_json(doc) << "\n"; std::cout << "SECOND: " << bsoncxx::to_json(doc2) << "\n"; } }
内容的提问来源于stack exchange,提问作者user2919828
相关产品推荐
相关产品推荐

