Pthread传递参数未获取预期值问题排查
问题分析与解决方案
这个问题的核心不是竞态条件,而是vector的内存重新分配导致了悬垂指针,让我们一步步拆解原因:
为什么会出现乱码输出?
当你调用A_vec.push_back(A(i))时,vector的工作机制是这样的:
- 第一次循环(i=0):vector初始容量通常为0,push_back会分配一块能容纳1个元素的内存,将
A(0)存入其中,此时&A_vec[0]指向这块内存的起始地址。你创建线程并传递这个指针。 - 第二次循环(i=1):vector当前容量是1,无法容纳第2个元素,因此会重新分配一块更大的内存,把原来的
A(0)移动/复制到新内存区域,然后释放旧内存。这时候,第一个线程持有的&A_vec[0]指针已经指向了被释放的内存(悬垂指针),当线程访问这个指针时,就会触发未定义行为,输出乱码。 - 而第二个线程传递的
&A_vec[1]是新分配内存中的地址,所以访问正常。
你误以为vector存储的元素相互独立就不会有问题,但忽略了vector在扩容时会迁移元素内存,导致旧指针失效的特性。
解决方案
这里有几种可靠的解决方式:
方案1:提前预留vector容量
在循环前调用reserve()方法,让vector提前分配足够的内存,避免扩容时的内存迁移:
#include <pthread.h> #include <vector> #include <cstdlib> #include <iostream> using namespace std; class A { public: A(int ID); int get_id() {return id;} private: int id; }; A::A(int ID): id(ID) {} void* basic_function( void* arg) { A *ptr = static_cast<A*>(arg); printf("A passed in with id %d\n", ptr->get_id()); return nullptr; // 线程函数返回nullptr避免编译器警告 } int main() { vector<pthread_t> thread_vec; vector<A> A_vec; A_vec.reserve(2); // 提前预留2个元素的容量,禁止扩容 for (int i=0; i< 2; ++i) { A_vec.push_back(A(i)); pthread_t tid; pthread_create(&tid, NULL, basic_function, (void*)(&(A_vec[i]))); thread_vec.push_back(tid); } void * ret; for (auto tid : thread_vec) pthread_join(tid, &ret); return 0; }
方案2:使用动态分配的对象
通过new创建A对象,让对象存储在堆内存中,避免vector扩容影响:
// 仅修改main和线程函数部分 void* basic_function( void* arg) { A *ptr = static_cast<A*>(arg); printf("A passed in with id %d\n", ptr->get_id()); delete ptr; // 用完后释放内存 return nullptr; } int main() { vector<pthread_t> thread_vec; for (int i=0; i< 2; ++i) { A* a_ptr = new A(i); // 堆上分配对象 pthread_t tid; pthread_create(&tid, NULL, basic_function, (void*)a_ptr); thread_vec.push_back(tid); } void * ret; for (auto tid : thread_vec) pthread_join(tid, &ret); return 0; }
方案3:使用智能指针管理内存
C++11及以上可以用std::unique_ptr或std::shared_ptr避免裸指针的内存管理问题:
#include <memory> // 引入智能指针头文件 void* basic_function( void* arg) { unique_ptr<A> ptr(static_cast<A*>(arg)); printf("A passed in with id %d\n", ptr->get_id()); return nullptr; } int main() { vector<pthread_t> thread_vec; for (int i=0; i< 2; ++i) { A* a_ptr = new A(i); pthread_t tid; pthread_create(&tid, NULL, basic_function, (void*)a_ptr); thread_vec.push_back(tid); } void * ret; for (auto tid : thread_vec) pthread_join(tid, &ret); return 0; }
内容的提问来源于stack exchange,提问作者HalfOfRain
相关产品推荐
相关产品推荐

