VS2019中使用std::vector<std::future<T>>时程序崩溃原因咨询
多线程哈希处理触发abort崩溃的问题修复
我尝试用C++实现多线程哈希处理输入消息流,代码如下,但在Visual Studio 2019调试时,程序仅创建少量线程就从abort.h触发崩溃。
原代码片段:
std::vector<std::future<HashData>> futures; std::vector<std::string> messages; for (int i = 0; i < messages.size(); i++) { std::promise<HashData> promiseHashData; std::future<HashData> futureHashData = promiseHashData.get_future(); futures.emplace_back(std::move(futureHashData)); std::async(std::launch::async, [&]() {PerformHash(std::move(promiseHashData), messages[i]);}); } std::vector<HashData> vectorOfHashData; // 等待所有异步任务完成 for (auto& futureObj : futures) { vectorOfHashData.push_back(futureObj.get()); }
void PerformHash(std::promise<HashData>&& promObject, std::string& message) { ComputeHashUsingSHA256(message); HashData data; // 为HashData对象赋值 data.i = i; data.blocks = blocks; data.blocksize = blocksize; data.blockbufs = blockbufs; data.secs = secs; memcpy(data.digest, digest, SHA256_DIGEST_SIZE); data.has_hashdata = has_hashdata; memcpy(data.hashdata_buf, hashdata_buf, c_hashsize); promObject.set_value(data); }
问题分析:
- 栈变量引用捕获失效:循环中的
promiseHashData是栈上局部变量,lambda用[&]捕获引用后,循环迭代会销毁旧promise并创建新的,异步任务执行时可能访问已销毁的promise,触发未定义行为导致崩溃。 - 字符串引用捕获错误:
messages[i]的引用在循环中会被后续迭代覆盖,异步任务可能访问到错误的字符串对象,甚至已失效的内存。 std::async返回值被丢弃:std::async返回的std::future如果被直接丢弃,会阻塞当前线程直到该异步任务完成,导致代码实际串行执行,还可能引发资源管理问题。- 未定义变量使用:
PerformHash函数中的i、blocks、digest等变量未定义,会导致编译或运行时错误。
修复方案:
- 改用移动捕获promise:将promise移动到lambda中,避免引用失效;同时按值捕获
messages[i],确保异步任务拿到独立的字符串副本。 - 保存
std::async的返回值:避免异步任务被阻塞,同时管理任务生命周期。 - 修正
PerformHash的参数和变量:将所需变量通过参数传递,或在函数内正确定义。
修复后的代码:
#include <vector> #include <future> #include <string> #include <cstring> // 假设HashData结构已定义 struct HashData { int i; int blocks; size_t blocksize; void* blockbufs; double secs; unsigned char digest[32]; // SHA256_DIGEST_SIZE bool has_hashdata; unsigned char hashdata_buf[64]; // 假设c_hashsize为64 }; // 修正哈希计算函数,直接填充HashData void ComputeHashUsingSHA256(const std::string& message, HashData& data) { // 此处实现实际的SHA256哈希逻辑,以下为模拟示例 data.blocks = 1; data.blocksize = message.size(); data.secs = 0.001; std::memset(data.digest, 0x00, sizeof(data.digest)); data.has_hashdata = true; std::memset(data.hashdata_buf, 0x01, sizeof(data.hashdata_buf)); } void PerformHash(std::promise<HashData>&& promObject, std::string message, int index) { HashData data; data.i = index; ComputeHashUsingSHA256(message, data); promObject.set_value(data); } int main() { std::vector<std::string> messages = {"msg1", "msg2", "msg3"}; std::vector<std::future<HashData>> futures; std::vector<std::future<void>> asyncFutures; // 保存async任务的future for (int i = 0; i < messages.size(); i++) { std::promise<HashData> promiseHashData; std::future<HashData> futureHashData = promiseHashData.get_future(); futures.emplace_back(std::move(futureHashData)); // 移动捕获promise,值捕获message和索引i asyncFutures.emplace_back(std::async(std::launch::async, [promise = std::move(promiseHashData), msg = messages[i], idx = i]() mutable { PerformHash(std::move(promise), msg, idx); })); } std::vector<HashData> vectorOfHashData; // 等待所有哈希结果返回 for (auto& futureObj : futures) { vectorOfHashData.push_back(futureObj.get()); } return 0; }
额外说明:
- 用
mutable修饰lambda,允许在lambda内移动捕获的promise对象。 - 调整
ComputeHashUsingSHA256的参数,直接传入HashData引用填充结果,避免函数内使用未定义变量。 - 保存
std::async返回的std::future<void>,确保异步任务能正常执行完毕,不会被提前阻塞。
内容的提问来源于stack exchange,提问作者devilsEye
相关产品推荐
相关产品推荐

