C++ pthread_create传递vector元素为线程参数结果异常问题
问题背景
学习C++多线程编程时,尝试将vector容器的元素地址作为参数传入pthread_create接口创建线程,预期两个线程分别打印ID为99、100的执行日志,但实际运行第二个线程打印ID为0,结果不符合预期。
原实现代码如下:
#include <iostream> #include <cstdlib> #include <pthread.h> #include <vector> using namespace std; void *count(void *arg) { int threadId = *((int *)arg); cout << "Currently thread with id " << threadId << " is executing " << endl; pthread_exit(NULL); } int main() { pthread_t thread1; vector<int> threadId(2); threadId[0] = 99; threadId[1] = 100; int retVal = pthread_create(&thread1, NULL, count, (void *)&threadId[0]); if (retVal) { cout << "Error in creating thread with Id: " << threadId[0] << endl; exit(-1); } pthread_t thread2; retVal = pthread_create(&thread2, NULL, count, (void *)&threadId[1]); if (retVal) { cout << "Error in creating thread with Id: " << threadId[1] << endl; exit(-1); } pthread_exit(NULL); }
实际运行输出:
Currently thread with id 99 is executing
Currently thread with id 0 is executing
问题根因
代码存在两个核心问题,直接导致参数值异常的是对象生命周期引发的野指针访问:
threadId是定义在main函数栈上的局部vector对象,主线程最后调用pthread_exit(NULL)终止自身时,会自动执行当前线程栈上所有局部对象的析构函数。vector析构时会释放其在堆上分配的存储元素的内存,此时两个子线程持有的参数地址指向的是已经被释放的内存,属于use-after-free的未定义行为,读到什么值都有可能,本次运行第二个参数读到0只是内存被释放后覆写的偶然结果,部分环境下甚至可能直接触发程序崩溃。- 额外隐患:
std::cout的流插入操作不是原子操作,多线程同时无锁调用会出现输出内容穿插、错乱的问题。
修复方案
核心是保证传入子线程的参数生命周期完全覆盖子线程的执行周期,同时解决输出竞态问题,最简单的修改方式是让主线程等待两个子线程执行完毕后再退出,不要提前终止:
#include <iostream> #include <cstdlib> #include <pthread.h> #include <vector> using namespace std; // 全局互斥锁保护cout输出 pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER; void *count(void *arg) { int threadId = *((int *)arg); pthread_mutex_lock(&print_lock); cout << "Currently thread with id " << threadId << " is executing " << endl; pthread_mutex_unlock(&print_lock); pthread_exit(NULL); } int main() { pthread_t thread1; vector<int> threadId(2); threadId[0] = 99; threadId[1] = 100; int retVal = pthread_create(&thread1, NULL, count, (void *)&threadId[0]); if (retVal) { cout << "Error in creating thread with Id: " << threadId[0] << endl; exit(-1); } pthread_t thread2; retVal = pthread_create(&thread2, NULL, count, (void *)&threadId[1]); if (retVal) { cout << "Error in creating thread with Id: " << threadId[1] << endl; exit(-1); } // 等待两个子线程执行完毕,保证vector生命周期覆盖子线程执行全程 pthread_join(thread1, NULL); pthread_join(thread2, NULL); // 子线程全部执行完再退出主线程 pthread_exit(NULL); }
其他可选的生命周期保障方式:
- 将存储线程ID的vector定义为全局变量,生命周期持续到进程退出
- 用
new动态分配每个线程的参数内存,子线程执行结束后自行delete释放 - 如果编写纯C代码,建议直接使用C11提供的
std::thread,可以通过值传递参数避免生命周期问题,不需要手动处理指针传参的隐患。
内容的提问来源于stack exchange,提问作者coder_01
相关产品推荐
相关产品推荐

