多线程环境下std vector重新申请空间导致的内存重复释放
多线程环境下std vector重新申请空间导致的内存重复释放
最近遇到了一个bug,自己第一时间没有注意到(但是manager看一眼就注意到了),因此在这里记录一下,加深印象
具体问题
首先说说具体情况,原本一个函数一直都没出问题,最近新添加了一个case之后,会在一处delete指针的地方crash,报错invalid pointer,下面是代码:
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
45
struct ThreadParam {
int* ptr_;
};
void threadFunc(void* param) {
/*...*/
// 线程内release memory
if(param->ptr_) {
delete param->ptr_;
param->ptr_ = nullptr;
}
}
void mainFunc() {
// 用于线程task的params
std::vector<ThreadParam> params;
params.reserve(256);
// for 循环创建线程任务
int count = 0;
for(/*....*/) {
ThreadParam param;
param.ptr_ = new int(10);
params.push_back(param);
thread->add(threadFunc, ¶ms[count]);
count++;
}
// 等待所有thread执行完毕
thread->waitAllJobs();
// 检查内存是否释放完毕
for(int i = 0; i < count; ++i) {
auto* ptr = params[i].ptr_;
if(ptr) {
delete ptr;
params[i].ptr_
}
}
}
这个函数最后会在mainFunc中delete ptr
处crash,原因是invalid pointer。
其实问题就出在 params.reserve(256)
以及 params.push_back(param)
这两处地方,std vector在进行push_back的时候,如果空间不够,会重新分配一块更大的连续内存空间,将原来的元素复制(或移动)到新的内存空间中,然后再将新元素添加到新内存空间的末尾,最后释放原来的内存空间。
造成的结果就是,线程内释放了ptr_,所有线程执行结束之后,主线程执行auto* ptr = params[i].ptr_
,拿到的ptr并不是被线程置为nullptr的指针,而是std vector扩容后拷贝过去的,所以它并不为空,但是指向的内存是已经被释放了的,最后造成了crash。
至于为什么之前的case并没有报错,原因是 params.reserve(256)
,之前的case没有进行扩容。
解决方法
将std::vector<ThreadParam> params
改为std::vector<ThreadParam*> params
即可解决。
This post is licensed under CC BY 4.0 by the author.