如何检查std::future是否为空?C++地图块生成异常排查
地图生成器的std::future空值与内存异常问题
问题描述
我使用C++结合glm、Perlin噪声和std::future开发地图生成器,用于生成地图块。运行一段时间后出现了空std::future的情况,不清楚空future的含义及检查方法,希望了解如何检查std::future是否为空,同时找出代码中导致问题的错误。
异常信息
Unhandled exception at 0x7623D6C2 in VoxelEngine.exe:
Microsoft C++ exception: std::bad_alloc at memory location 0x00DDF290.
相关代码
void ChunkManager::update(float deltaTime) { glm::vec3 cameraPosition = mainCamera->transform.getPosition(); glm::i32vec3 newCameraChunk = cameraPosition / chunkSize; if (newCameraChunk != cameraChunk) { cameraChunk = newCameraChunk; loadChunks(); } if (loading) { handleLoadingChunks(); } } void ChunkManager::loadChunks() { glm::i32vec3 minChunk(cameraChunk.x - visibleRange, 0, cameraChunk.z - visibleRange); float offset = Chunk::CHUNK_WIDTH * 2 * Block::BLOCK_SIZE; for (int x = 0; x < visibleRange*2; x++) { for (int z = 0; z < visibleRange*2; z++) { loadingChunks.push_back(std::async(std::launch::async, [&, x, z, minChunk, offset]() {return createChunk(x, 0, z, minChunk, offset, resourceManager); })); } } loading = true; } Chunk* ChunkManager::createChunk(int x, int y, int z, glm::i32vec3 minChunk, float offset, ResourceManager* resourceManager) { Chunk* chunk = new Chunk(material, x + minChunk.x, y + minChunk.y, z + minChunk.z); chunk->InitializeChunk(noiseGenerator); chunk->CreateMesh(resourceManager); chunk->position(glm::vec3((x + minChunk.x) * offset, 0, (z + minChunk.z) * offset)); return chunk; } void ChunkManager::handleLoadingChunks() { bool allDone = true; for (auto& it : loadingChunks) { if (it.wait_for(std::chrono::seconds(0)) != std::future_status::ready) { allDone = false; } } if (allDone) { for (auto it : chunks) { delete it; } chunks.clear(); for (auto& it : loadingChunks) { if (it.valid()) { Chunk* chunk = it.get(); // 此处经常出现空值问题 chunk->FinalizeMesh(); chunks.push_back(chunk); } } loadingChunks.clear(); loading = false; } } class ChunkManager : public GameObject { public: ChunkManager(Material* material, Camera* camera, ResourceManager* resourceManager, NoiseGenerator* noiseGenerator); ~ChunkManager(); void render(glm::mat4x4 projectionMatrix, glm::mat4x4 viewMatrix, glm::mat4x4 parentTransform); void update(float deltaTime); private: std::vector<Chunk*> chunks; std::vector<std::future<Chunk*>> loadingChunks; glm::vec3 minCorner; glm::i32vec3 cameraChunk; void loadChunks(); Chunk* createChunk(int x, int y, int z, glm::i32vec3 minChunk, float offset, ResourceManager* resourceManager); void handleLoadingChunks(); float chunkSize; int visibleRange = 7; bool loading = false; Material* material; ResourceManager* resourceManager; NoiseGenerator* noiseGenerator; };
补充排查信息
我找到了可能的错误原因,但不知道如何修复。似乎是std::future中创建的Chunk对象尝试返回主线程时,主线程内存不足。
问题解答
1. 检查std::future是否为空的方法
使用std::future::valid()成员函数,它会返回布尔值:
- 返回
true:表示该future关联了有效的异步任务,可安全调用get()获取结果。 - 返回
false:表示该future处于空状态(未关联任务、结果已被获取或已被移动)。你代码中已经用到了这个方法,逻辑是正确的。
2. 代码中的核心问题分析
(1)内存过载与泄漏
- 重复加载导致内存堆积:相机每次移动都会触发
loadChunks(),如果前一次加载未完成(loading仍为true),新的异步任务会不断被添加到loadingChunks中。当visibleRange=7时,单次生成14×14=196个Chunk,多次触发后内存会被快速耗尽,最终抛出std::bad_alloc。 - 裸指针的内存管理风险:代码使用裸指针存储Chunk对象,若
Chunk的析构函数未正确释放内部资源(比如Mesh顶点数据、GPU缓冲区等),会导致内存泄漏,日积月累耗尽系统内存。
(2)std::future空值的诱因
- 系统资源不足:当系统无法创建新线程时,
std::async可能返回无效的future,此时valid()返回false。 - 异步任务内部抛出异常:如果
createChunk中new Chunk或CreateMesh时内存不足,会抛出std::bad_alloc,该异常会被future捕获,调用get()时会重新抛出,容易被误判为future为空。
3. 修复方案
(1)避免重复触发加载
在update函数中,触发新加载前确保当前任务已完成:
if (newCameraChunk != cameraChunk) { // 等待当前加载完成再启动新任务,避免内存堆积 while (loading) { handleLoadingChunks(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 避免阻塞主线程 } cameraChunk = newCameraChunk; loadChunks(); }
(2)限制异步任务并发数
不要一次性创建大量线程,改用线程池控制并发数,或每次启动固定数量的任务。比如将std::launch::async改为std::launch::deferred结合手动调度,避免系统线程资源耗尽。
(3)优化内存管理
- 使用智能指针:将
std::vector<Chunk*>改为std::vector<std::unique_ptr<Chunk>>,std::future<Chunk*>改为std::future<std::unique_ptr<Chunk>>,避免手动管理内存导致的泄漏:std::unique_ptr<Chunk> ChunkManager::createChunk(int x, int y, int z, glm::i32vec3 minChunk, float offset, ResourceManager* resourceManager) { auto chunk = std::make_unique<Chunk>(material, x + minChunk.x, y + minChunk.y, z + minChunk.z); chunk->InitializeChunk(noiseGenerator); chunk->CreateMesh(resourceManager); chunk->position(glm::vec3((x + minChunk.x) * offset, 0, (z + minChunk.z) * offset)); return chunk; } - 确保Chunk析构函数正确释放资源:检查
Chunk类的析构函数,确保所有动态分配的内存、GPU资源都被正确释放。
(4)添加异常处理
在调用get()时捕获异常,避免程序崩溃:
for (auto& it : loadingChunks) { if (it.valid()) { try { auto chunk = it.get(); if (chunk) { chunk->FinalizeMesh(); chunks.push_back(std::move(chunk)); } } catch (const std::bad_alloc& e) { std::cerr << "Chunk创建失败:内存不足 - " << e.what() << std::endl; } catch (...) { std::cerr << "Chunk创建失败:未知异常" << std::endl; } } }
(5)优化Chunk替换逻辑
不要每次全量删除旧Chunk,对比新的可见范围,只删除不在范围内的Chunk,保留仍需要的Chunk,减少内存分配和释放的开销。
内容的提问来源于stack exchange,提问作者Marko Taht
相关产品推荐
相关产品推荐

