C++多线程程序意外终止求助:多设备消息发送未完成
问题排查与修复方案
你的程序莫名终止的核心原因有几个,逐一分析并给出修复:
1. 迭代器引用捕获导致的未定义行为
在async的lambda中,你用[&]捕获了循环迭代器it的引用,但循环过程中it会被不断递增,线程启动后很可能it已经指向mapDevtoMessage.end(),此时访问it->second或it->first会触发未定义行为,直接导致程序崩溃或异常终止。
修复方案:捕获迭代器的副本,或者直接捕获当前循环的元素值:
vector<future<void>> futures; // 用范围for循环更安全,避免迭代器引用问题 for(auto& pair : mapDevtoMessage){ futures.push_back(async(launch::async, [&vec=pair.second, name=pair.first]{ send_messages(vec, name); })); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } for(auto &its : futures){ its.wait(); }
如果坚持使用迭代器循环,可捕获当前元素的副本:
vector<future<void>> futures; auto it = mapDevtoMessage.begin(); for(; it != mapDevtoMessage.end(); ++it){ auto current_pair = *it; // 复制当前元素,避免引用失效 futures.push_back(async(launch::async, [&]{ send_messages(current_pair.second, current_pair.first); })); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } for(auto &its : futures){ its.wait(); }
2. 未处理的错误引发程序终止
当socket()、connect()调用失败时,你的代码仅打印错误但继续执行后续逻辑:
- 若
connect()失败,后续调用send()时会传入无效的文件描述符,触发SIGPIPE信号,默认信号处理逻辑是直接终止程序。 - 未检查
send()的返回值,发送失败时也会引发异常或未定义行为。
修复方案:错误发生后立即退出函数,避免后续无效操作:
void send_messages (vector<string>& vec, string name){ mtx.lock(); PORT++; int current_port = PORT; // 保存当前端口,避免PORT后续变化影响日志 mtx.unlock(); int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { mtx.lock(); printf("Socket creation error for device %s\n", name.c_str()); mtx.unlock(); return; } struct sockaddr_in serv_addr{}; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(current_port); if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { mtx.lock(); printf("Invalid address for device %s, port %d\n", name.c_str(), current_port); mtx.unlock(); close(sock); return; } // connect返回0表示成功,失败返回-1,无需将返回值赋值给client_fd if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { mtx.lock(); printf("Connection Failed for device %s, port %d\n", name.c_str(), current_port); mtx.unlock(); close(sock); return; } for(auto &i : vec){ string val = hexToASCII(i); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 用字符串实际长度代替硬写的100,避免内存越界 ssize_t send_len = send(sock, val.c_str(), val.size(), 0); mtx.lock(); if(send_len < 0){ printf("Send failed for message %s from %s\n", i.c_str(), name.c_str()); } else { printf("Message %s from %s sent (port %d)\n", i.c_str(), name.c_str(), current_port); } mtx.unlock(); if(send_len < 0) break; // 发送失败则终止后续发送 } close(sock); // 关闭通信socket }
3. PORT变量的边界检查
服务器仅开放10000个端口,需确保PORT递增不会超出范围,否则后续connect必然失败,甚至可能重复使用端口。
修复方案:添加PORT边界检查:
mtx.lock(); // 假设服务器起始端口为START_PORT,最大端口为START_PORT+9999 const int START_PORT = 8000; const int MAX_PORT = START_PORT + 9999; if(PORT > MAX_PORT){ mtx.unlock(); mtx.lock(); printf("No available ports for device %s\n", name.c_str()); mtx.unlock(); return; } PORT++; int current_port = PORT; mtx.unlock();
4. SIGPIPE信号处理(可选)
若不想因send失败触发程序终止,可在程序启动时忽略SIGPIPE信号:
#include <signal.h> int main(){ signal(SIGPIPE, SIG_IGN); // 忽略SIGPIPE信号,send失败仅返回-1 // ... 其他初始化代码 }
内容的提问来源于stack exchange,提问作者malviska
相关产品推荐
相关产品推荐

