在C++多线程开发过程中难免会遇到很多意想不到的问题,最近遇到了参数传递的问题,总结一下。
参数传递如果是临时变量,就用malloc或new来申请变量,不然在线程运行时,临时变量可能会被释放,但是线程里面的是野指针。
举例 代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <iostream> #include <pthread.h> #include <vector> using namespace std;
void *func(void *args) { vector<int> *vec = (vector<int>*) args; cout<<"vec size:"<<vec->size()<<endl; for(vector<int>::iterator it = vec->begin();it!=vec->end();it++) { cout<<"输出 "<<*it<<endl; }
}
int main() { pthread_t thread_ids[2]; for(int i=0;i<2;i++) { vector<int> vec; for(int j=0;j<3;j++) { vec.push_back(i*j); }
int ret=pthread_create(&thread_ids[i],NULL,func,(void*)&vec); if(ret!=0) { cout<<"Create Thread Fail!"<<endl; return -1; }
}
for(int i=0;i<2;i++) { pthread_join(thread_ids[i],NULL); }
return 0; }
|
结果输出

与预期相差很多。
原因分析:
vec是临时变量,作为参数传递给线程。pthread_create会立刻返回,第一层for循环会很快结束,vec会被释放,此时线程拿到的参数是个野指针,输出结果就不是预期了。
解决办法,临时变量vec是new或者malloc出来,但是要注意内存泄漏的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <iostream> #include <pthread.h> #include <vector> using namespace std;
void *func(void *args) { vector<int> *vec = (vector<int>*) args; cout<<"vec size:"<<vec->size()<<endl; for(vector<int>::iterator it = vec->begin();it!=vec->end();it++) { cout<<*it<<endl; }
}
int main() { pthread_t thread_ids[2]; for(int i=0;i<2;i++) { vector<int> *vec = new vector<int>(); for(int j=0;j<3;j++) { vec->push_back(i*j); }
int ret=pthread_create(&thread_ids[i],NULL,func,(void*)vec); if(ret!=0) { cout<<"Create Thread Fail!"<<endl; return -1; } }
for(int i=0;i<2;i++) { pthread_join(thread_ids[i],NULL); }
return 0; }
|
输出结果如下图,是正确的:

phread_join会等线程结束,正确使用pthread_join可以防止临时变量被提前释放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <iostream> #include <pthread.h> #include <vector>
using namespace std;
void *func(void *args) { vector<int> *vec = (vector<int> *) args; cout << "vec size:" << vec->size() << endl; for (vector<int>::iterator it = vec->begin(); it != vec->end(); it++) { cout << "输出 " << *it << endl; }
}
void test() { pthread_t thread_id;
vector<int> vec; for (int j = 0; j < 3; j++) { vec.push_back(j); }
int ret = pthread_create(&thread_id, NULL, func, (void *) &vec); if (ret != 0) { cout << "Create Thread Fail!" << endl; return; }
}
int main() { test(); return 0; }
|
vec离开test()函数也会被释放掉,导致程序出现不可预期的错误。
如果在test()中加上pthread_join函数,在test()中等待线程结束,此时vec一直有效,输出结果就是正确的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream> #include <pthread.h> #include <vector>
using namespace std;
void *func(void *args) { vector<int> *vec = (vector<int> *) args; cout << "vec size:" << vec->size() << endl; for (vector<int>::iterator it = vec->begin(); it != vec->end(); it++) { cout << "输出 " << *it << endl; }
}
void test() { pthread_t thread_id;
vector<int> vec; for (int j = 0; j < 3; j++) { vec.push_back(j); }
int ret = pthread_create(&thread_id, NULL, func, (void *) &vec); if (ret != 0) { cout << "Create Thread Fail!" << endl; return; }
pthread_join(thread_id,NULL);
}
int main() { test(); return 0; }
|
谨慎使用STL作为线程参数
在使用vector等STL时,如果传递给线程参数的其中的某个元素地址,由于vector会根据元素多少动态申请内存,之前传递给线程的地址就有可能会失效,导致了非预期效果出现。
看下面一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <iostream> #include <pthread.h> #include <vector> #include <zconf.h>
using namespace std;
void *func(void *args) { int *val = (int *)args; sleep(1); cout<<*val<<endl; }
int main() {
pthread_t thread_ids[5];
vector<int> vec; for (int i = 0; i < 5; i++) { cout<<"capactity:"<<vec.capacity()<<endl; vec.push_back(i); int ret = pthread_create(&thread_ids[i], NULL, func, (void *) &(vec[i])); if (ret != 0) { cout << "Create Thread Fail!" << endl; return -1; }
}
for(int i=0;i<5;i++) { pthread_join(thread_ids[i], NULL); }
return 0; }
|
输出结果:

可以发现输出的与预期差很多。
这个错误比较隐蔽:当vec中只有0,1的时候,capacity大小为2,此时空间已经满了。vec.push_back(2),会重新申请空间,此时之前传递给pthread_create的vec[1]的地址就会失效,此时打印就会出错。
也会有人问,如果传递的迭代器呢,会不会出现这个问题?
在STL源码剖析这本书中,提到过对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器都失效了。所以传递迭代器也会出现这个问题。这个问题也引申出来了,在使用vector作为外层循环的时候不要在循环中会引起空间重新配置的操作。