使用 std::shared_ptr 时出现 malloc 错误
malloc error when using std::shared_ptr
我已经决定 ide 并行化我编写的一个巨大的程序,最终我遇到了新的 C++11 智能指针。
我有一个应该执行很多次(通常超过 1000 次)的例程,这有点昂贵。它是 运行 在一个非常简单的 for 循环中,我所做的是将这个 for 循环安装在一个方法中,该方法将被一些工作线程运行。
这样做了,用 std::shared_ptr
包装了一些参数,考虑了竞争条件,一切似乎都很好。
但是现在,有时进程会中止,并且会出现以下错误之一:
Process finished with exit code 139 (interrupted by signal 11:
SIGSEGV)
或
malloc.c:2395: sysmalloc: Assertion `(old_top == initial_top (av) &&
old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse
(old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
所有这些错误都是在并行进行时发生的;不是之前,不是刚开始,不是最后,而是介于两者之间,这让我感觉像是未涵盖的竞争条件。
程序很大,但我创建了一个能够重现问题的缩影:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <thread>
#include <set>
#include <memory>
#include <atomic>
namespace std {
template <>
struct hash<std::multiset<unsigned long>>
{
std::size_t operator()(const std::multiset<unsigned long>& k) const
{
std::size_t r = 0;
bool shift = false;
for (auto&& it : k) {
r = (r >> !shift) ^ (std::hash<unsigned long>()(it) << shift);
shift = !shift;
}
return r;
}
};
}
typedef std::unordered_map<std::multiset<unsigned long>, int*> graphmap;
std::multiset<unsigned long>* bar(int pos) {
std::multiset<unsigned long> *label = new std::multiset<unsigned long>;
label->insert(pos%5);
label->insert(pos%2);
return label;
}
void foo(std::shared_ptr<graphmap> &kSubgraphs, int pos) {
int *v = (*kSubgraphs)[*bar(pos)];
if(v == nullptr) {
v = new int[pos+1]();
v[0]++;
} else {
v[pos]++;
}
}
void worker(std::shared_ptr<std::atomic_int> *counter, int n, std::shared_ptr<graphmap> *kSubgraphs)
{
for (int pos = (*counter)->fetch_add(1); pos <= n; pos = (*counter)->fetch_add(1)) {
if (pos%100==0) std::cout << pos << std::endl;
foo(*kSubgraphs, pos);
}
}
int main() {
int n = 1000;
std::vector<std::thread> threads;
std::shared_ptr<graphmap> kSubgraphs = std::make_shared<graphmap>();
std::shared_ptr<std::atomic_int> counter = std::make_shared<std::atomic_int>(0);
for (int i=0; i<5; i++) {
foo(kSubgraphs, n);
}
for (int i=0; i<4; i++) {
threads.push_back(std::thread(worker, &counter, n, &kSubgraphs));
}
for(auto& th : threads) th.join();
return 0;
}
这段代码基本上模仿了原始代码的行为,即使用由 multiset 键控的 unordered_map值是指向 int 数组的指针。首先插入一些键并初始化数组(也许问题是由我初始化它的方式引起的?)最后工作线程 运行 更新 [=49= 的条目数组的唯一位置]unordered_map.
两个线程可以同时访问同一个映射条目,但它们绝不会同时写入数组的同一个索引。
与原始代码不同,此代码在 运行 网络编译器(例如 ideone.com 上 运行 时不会抛出任何错误,我也尝试 运行 从 CLion ide 并且不会发生错误(如果尝试足够多次可能会发生错误),但是当 运行 从命令行多次使用时,我得到了与原始错误类似的错误。我编译它:
g++ -std=c++11 -pthread -o test.exe test.cpp
在 运行ning 几次之后,它最终给出了这个错误:
*** Error in `./test.exe': double free or corruption (fasttop): 0x00000000006a2d30 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7fccc9d4f725]
/lib/x86_64-linux-gnu/libc.so.6(+0x7ff4a)[0x7fccc9d57f4a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fccc9d5babc]
./test.exe[0x404e9e]
./test.exe[0x40431b]
./test.exe[0x4045ed]
./test.exe[0x407c6c]
./test.exe[0x4078d6]
./test.exe[0x40742a]
./test.exe[0x40869e]
./test.exe[0x4086be]
./test.exe[0x4085dd]
./test.exe[0x40842d]
./test.exe[0x4023a2]
./test.exe[0x401d55]
./test.exe[0x401c4a]
./test.exe[0x401c66]
./test.exe[0x401702]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fccc9cf8830]
./test.exe[0x401199]
======= Memory map: ========
00400000-0040f000 r-xp 00000000 08:05 12202697 /home/rodrigo/test.exe
0060e000-0060f000 r--p 0000e000 08:05 12202697 /home/rodrigo/test.exe
0060f000-00610000 rw-p 0000f000 08:05 12202697 /home/rodrigo/test.exe
00691000-006c3000 rw-p 00000000 00:00 0 [heap]
7fcca8000000-7fcca8089000 rw-p 00000000 00:00 0
7fcca8089000-7fccac000000 ---p 00000000 00:00 0
7fccb0000000-7fccb008b000 rw-p 00000000 00:00 0
7fccb008b000-7fccb4000000 ---p 00000000 00:00 0
7fccb8000000-7fccb8089000 rw-p 00000000 00:00 0
7fccb8089000-7fccbc000000 ---p 00000000 00:00 0
7fccc0000000-7fccc007c000 rw-p 00000000 00:00 0
7fccc007c000-7fccc4000000 ---p 00000000 00:00 0
7fccc79cb000-7fccc79cc000 ---p 00000000 00:00 0
7fccc79cc000-7fccc81cc000 rw-p 00000000 00:00 0
7fccc81cc000-7fccc81cd000 ---p 00000000 00:00 0
7fccc81cd000-7fccc89cd000 rw-p 00000000 00:00 0
7fccc89cd000-7fccc89ce000 ---p 00000000 00:00 0
7fccc89ce000-7fccc91ce000 rw-p 00000000 00:00 0
7fccc91ce000-7fccc91cf000 ---p 00000000 00:00 0
7fccc91cf000-7fccc99cf000 rw-p 00000000 00:00 0
7fccc99cf000-7fccc9ad7000 r-xp 00000000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9ad7000-7fccc9cd6000 ---p 00108000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9cd6000-7fccc9cd7000 r--p 00107000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9cd7000-7fccc9cd8000 rw-p 00108000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9cd8000-7fccc9e98000 r-xp 00000000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccc9e98000-7fccca097000 ---p 001c0000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccca097000-7fccca09b000 r--p 001bf000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccca09b000-7fccca09d000 rw-p 001c3000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccca09d000-7fccca0a1000 rw-p 00000000 00:00 0
7fccca0a1000-7fccca0b9000 r-xp 00000000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca0b9000-7fccca2b8000 ---p 00018000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca2b8000-7fccca2b9000 r--p 00017000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca2b9000-7fccca2ba000 rw-p 00018000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca2ba000-7fccca2be000 rw-p 00000000 00:00 0
7fccca2be000-7fccca2d4000 r-xp 00000000 08:05 24121519 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fccca2d4000-7fccca4d3000 ---p 00016000 08:05 24121519 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fccca4d3000-7fccca4d4000 rw-p 00015000 08:05 24121519 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fccca4d4000-7fccca646000 r-xp 00000000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca646000-7fccca846000 ---p 00172000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca846000-7fccca850000 r--p 00172000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca850000-7fccca852000 rw-p 0017c000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca852000-7fccca856000 rw-p 00000000 00:00 0
7fccca856000-7fccca87c000 r-xp 00000000 08:05 24126370 /lib/x86_64-linux-gnu/ld-2.23.so
7fcccaa44000-7fcccaa4a000 rw-p 00000000 00:00 0
7fcccaa78000-7fcccaa7b000 rw-p 00000000 00:00 0
7fcccaa7b000-7fcccaa7c000 r--p 00025000 08:05 24126370 /lib/x86_64-linux-gnu/ld-2.23.so
7fcccaa7c000-7fcccaa7d000 rw-p 00026000 08:05 24126370 /lib/x86_64-linux-gnu/ld-2.23.so
7fcccaa7d000-7fcccaa7e000 rw-p 00000000 00:00 0
7ffc6b1c8000-7ffc6b1e9000 rw-p 00000000 00:00 0 [stack]
7ffc6b1fa000-7ffc6b1fc000 r--p 00000000 00:00 0 [vvar]
7ffc6b1fc000-7ffc6b1fe000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
[1] 26639 abort (core dumped) ./test.exe
最后,当 运行在 CLion 中打开调试模式的原始代码设置为捕获异常作为断点时,它显示 SIGBUS(Bus error)
的信号中断或 SIGSEGV(Segmentation fault)
有时,在中断映射到该行之前执行的最后一行代码:
int *v = (*kSubgraphs)[*bar(pos)];
在此处显示的代码的 foo
函数中。
我对这个有点迷茫。我最强烈的假设是我以错误的方式使用智能指针,尽管我看不到在哪里。
你的程序有未定义的行为,因为你从不同的线程访问 kSubgraphs
指向的对象,没有互斥。这发生在函数 foo
的这一行代码中,它是从您的线程函数 worker
:
中调用的
int *v = (*kSubgraphs)[*bar(pos)];
我猜不出您为什么认为 shared_ptr
会对您的案子有任何帮助。按照您的方式,将对象包装在 shared_ptr
.
中是完全没有意义的
shared_ptr
当您的对象的所有权与其他不同的对象共享时使用。与"sharing objects between threads".
无关
我已经决定 ide 并行化我编写的一个巨大的程序,最终我遇到了新的 C++11 智能指针。
我有一个应该执行很多次(通常超过 1000 次)的例程,这有点昂贵。它是 运行 在一个非常简单的 for 循环中,我所做的是将这个 for 循环安装在一个方法中,该方法将被一些工作线程运行。
这样做了,用 std::shared_ptr
包装了一些参数,考虑了竞争条件,一切似乎都很好。
但是现在,有时进程会中止,并且会出现以下错误之一:
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
或
malloc.c:2395: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
所有这些错误都是在并行进行时发生的;不是之前,不是刚开始,不是最后,而是介于两者之间,这让我感觉像是未涵盖的竞争条件。
程序很大,但我创建了一个能够重现问题的缩影:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <thread>
#include <set>
#include <memory>
#include <atomic>
namespace std {
template <>
struct hash<std::multiset<unsigned long>>
{
std::size_t operator()(const std::multiset<unsigned long>& k) const
{
std::size_t r = 0;
bool shift = false;
for (auto&& it : k) {
r = (r >> !shift) ^ (std::hash<unsigned long>()(it) << shift);
shift = !shift;
}
return r;
}
};
}
typedef std::unordered_map<std::multiset<unsigned long>, int*> graphmap;
std::multiset<unsigned long>* bar(int pos) {
std::multiset<unsigned long> *label = new std::multiset<unsigned long>;
label->insert(pos%5);
label->insert(pos%2);
return label;
}
void foo(std::shared_ptr<graphmap> &kSubgraphs, int pos) {
int *v = (*kSubgraphs)[*bar(pos)];
if(v == nullptr) {
v = new int[pos+1]();
v[0]++;
} else {
v[pos]++;
}
}
void worker(std::shared_ptr<std::atomic_int> *counter, int n, std::shared_ptr<graphmap> *kSubgraphs)
{
for (int pos = (*counter)->fetch_add(1); pos <= n; pos = (*counter)->fetch_add(1)) {
if (pos%100==0) std::cout << pos << std::endl;
foo(*kSubgraphs, pos);
}
}
int main() {
int n = 1000;
std::vector<std::thread> threads;
std::shared_ptr<graphmap> kSubgraphs = std::make_shared<graphmap>();
std::shared_ptr<std::atomic_int> counter = std::make_shared<std::atomic_int>(0);
for (int i=0; i<5; i++) {
foo(kSubgraphs, n);
}
for (int i=0; i<4; i++) {
threads.push_back(std::thread(worker, &counter, n, &kSubgraphs));
}
for(auto& th : threads) th.join();
return 0;
}
这段代码基本上模仿了原始代码的行为,即使用由 multiset 键控的 unordered_map值是指向 int 数组的指针。首先插入一些键并初始化数组(也许问题是由我初始化它的方式引起的?)最后工作线程 运行 更新 [=49= 的条目数组的唯一位置]unordered_map.
两个线程可以同时访问同一个映射条目,但它们绝不会同时写入数组的同一个索引。
与原始代码不同,此代码在 运行 网络编译器(例如 ideone.com 上 运行 时不会抛出任何错误,我也尝试 运行 从 CLion ide 并且不会发生错误(如果尝试足够多次可能会发生错误),但是当 运行 从命令行多次使用时,我得到了与原始错误类似的错误。我编译它:
g++ -std=c++11 -pthread -o test.exe test.cpp
在 运行ning 几次之后,它最终给出了这个错误:
*** Error in `./test.exe': double free or corruption (fasttop): 0x00000000006a2d30 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7fccc9d4f725]
/lib/x86_64-linux-gnu/libc.so.6(+0x7ff4a)[0x7fccc9d57f4a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fccc9d5babc]
./test.exe[0x404e9e]
./test.exe[0x40431b]
./test.exe[0x4045ed]
./test.exe[0x407c6c]
./test.exe[0x4078d6]
./test.exe[0x40742a]
./test.exe[0x40869e]
./test.exe[0x4086be]
./test.exe[0x4085dd]
./test.exe[0x40842d]
./test.exe[0x4023a2]
./test.exe[0x401d55]
./test.exe[0x401c4a]
./test.exe[0x401c66]
./test.exe[0x401702]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fccc9cf8830]
./test.exe[0x401199]
======= Memory map: ========
00400000-0040f000 r-xp 00000000 08:05 12202697 /home/rodrigo/test.exe
0060e000-0060f000 r--p 0000e000 08:05 12202697 /home/rodrigo/test.exe
0060f000-00610000 rw-p 0000f000 08:05 12202697 /home/rodrigo/test.exe
00691000-006c3000 rw-p 00000000 00:00 0 [heap]
7fcca8000000-7fcca8089000 rw-p 00000000 00:00 0
7fcca8089000-7fccac000000 ---p 00000000 00:00 0
7fccb0000000-7fccb008b000 rw-p 00000000 00:00 0
7fccb008b000-7fccb4000000 ---p 00000000 00:00 0
7fccb8000000-7fccb8089000 rw-p 00000000 00:00 0
7fccb8089000-7fccbc000000 ---p 00000000 00:00 0
7fccc0000000-7fccc007c000 rw-p 00000000 00:00 0
7fccc007c000-7fccc4000000 ---p 00000000 00:00 0
7fccc79cb000-7fccc79cc000 ---p 00000000 00:00 0
7fccc79cc000-7fccc81cc000 rw-p 00000000 00:00 0
7fccc81cc000-7fccc81cd000 ---p 00000000 00:00 0
7fccc81cd000-7fccc89cd000 rw-p 00000000 00:00 0
7fccc89cd000-7fccc89ce000 ---p 00000000 00:00 0
7fccc89ce000-7fccc91ce000 rw-p 00000000 00:00 0
7fccc91ce000-7fccc91cf000 ---p 00000000 00:00 0
7fccc91cf000-7fccc99cf000 rw-p 00000000 00:00 0
7fccc99cf000-7fccc9ad7000 r-xp 00000000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9ad7000-7fccc9cd6000 ---p 00108000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9cd6000-7fccc9cd7000 r--p 00107000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9cd7000-7fccc9cd8000 rw-p 00108000 08:05 24126366 /lib/x86_64-linux-gnu/libm-2.23.so
7fccc9cd8000-7fccc9e98000 r-xp 00000000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccc9e98000-7fccca097000 ---p 001c0000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccca097000-7fccca09b000 r--p 001bf000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccca09b000-7fccca09d000 rw-p 001c3000 08:05 24126374 /lib/x86_64-linux-gnu/libc-2.23.so
7fccca09d000-7fccca0a1000 rw-p 00000000 00:00 0
7fccca0a1000-7fccca0b9000 r-xp 00000000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca0b9000-7fccca2b8000 ---p 00018000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca2b8000-7fccca2b9000 r--p 00017000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca2b9000-7fccca2ba000 rw-p 00018000 08:05 24126373 /lib/x86_64-linux-gnu/libpthread-2.23.so
7fccca2ba000-7fccca2be000 rw-p 00000000 00:00 0
7fccca2be000-7fccca2d4000 r-xp 00000000 08:05 24121519 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fccca2d4000-7fccca4d3000 ---p 00016000 08:05 24121519 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fccca4d3000-7fccca4d4000 rw-p 00015000 08:05 24121519 /lib/x86_64-linux-gnu/libgcc_s.so.1
7fccca4d4000-7fccca646000 r-xp 00000000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca646000-7fccca846000 ---p 00172000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca846000-7fccca850000 r--p 00172000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca850000-7fccca852000 rw-p 0017c000 08:05 6029347 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21
7fccca852000-7fccca856000 rw-p 00000000 00:00 0
7fccca856000-7fccca87c000 r-xp 00000000 08:05 24126370 /lib/x86_64-linux-gnu/ld-2.23.so
7fcccaa44000-7fcccaa4a000 rw-p 00000000 00:00 0
7fcccaa78000-7fcccaa7b000 rw-p 00000000 00:00 0
7fcccaa7b000-7fcccaa7c000 r--p 00025000 08:05 24126370 /lib/x86_64-linux-gnu/ld-2.23.so
7fcccaa7c000-7fcccaa7d000 rw-p 00026000 08:05 24126370 /lib/x86_64-linux-gnu/ld-2.23.so
7fcccaa7d000-7fcccaa7e000 rw-p 00000000 00:00 0
7ffc6b1c8000-7ffc6b1e9000 rw-p 00000000 00:00 0 [stack]
7ffc6b1fa000-7ffc6b1fc000 r--p 00000000 00:00 0 [vvar]
7ffc6b1fc000-7ffc6b1fe000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
[1] 26639 abort (core dumped) ./test.exe
最后,当 运行在 CLion 中打开调试模式的原始代码设置为捕获异常作为断点时,它显示 SIGBUS(Bus error)
的信号中断或 SIGSEGV(Segmentation fault)
有时,在中断映射到该行之前执行的最后一行代码:
int *v = (*kSubgraphs)[*bar(pos)];
在此处显示的代码的 foo
函数中。
我对这个有点迷茫。我最强烈的假设是我以错误的方式使用智能指针,尽管我看不到在哪里。
你的程序有未定义的行为,因为你从不同的线程访问 kSubgraphs
指向的对象,没有互斥。这发生在函数 foo
的这一行代码中,它是从您的线程函数 worker
:
int *v = (*kSubgraphs)[*bar(pos)];
我猜不出您为什么认为 shared_ptr
会对您的案子有任何帮助。按照您的方式,将对象包装在 shared_ptr
.
shared_ptr
当您的对象的所有权与其他不同的对象共享时使用。与"sharing objects between threads".