为指针 C++ 赋值时出现分段错误
Segmentation Fault when assigning value to a pointer C++
当我 运行 以下并行代码时,我在第 18 行(两次打印之间)的分配处遇到分段错误。我不太明白是什么原因造成的。
这是描述问题的最小工作示例:
#include <iostream>
#include <numeric>
#include <vector>
#include <thread>
struct Worker{
std::vector<int>* v;
void f(){
std::vector<int> a(20);
std::iota(a.begin(), a.end(), 1);
auto b = new std::vector<int>(a);
std::cout << "Test 1" << std::endl;
v = b;
std::cout << "Test 2" << std::endl;
}
};
int main(int argc, char** argv) {
int nw = 1;
std::vector<std::thread> threads(nw);
std::vector<std::unique_ptr<Worker>> W;
for(int i = 0; i < nw; i++){
W.push_back(std::make_unique<Worker>());
threads[i] = std::thread([&]() { W[i]->f(); } );
// Pinning threads to cores
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(i, &cpuset);
pthread_setaffinity_np(threads[i].native_handle(), sizeof(cpu_set_t), &cpuset);
}
for (int i = 0; i < nw; i++) {
threads[i].join();
std::cout << (*(W[i]->v))[0] << std::endl;
}
}
似乎用 -fsanitize=address 编译它,代码工作正常,但我的性能最差。我怎样才能让它发挥作用?
std::vector
不是线程安全的。 None C++ 库中的容器是线程安全的。
threads[i] = std::thread([&]() { W[i]->f(); } );
新的执行线程通过引用捕获向量并访问它。
W.push_back(std::make_unique<Worker>());
原始执行线程不断修改此处的向量,而没有与任何新执行线程同步对W
向量的访问。任何 push_back
都可能使 vector 的现有内容无效以便重新分配它,并且如果不同的执行线程试图同时获取 W[i]
,在它被重新分配时,就会出现 hillarity。
这是未定义的行为。
您必须要么使用互斥锁同步对向量的访问,要么使用任意数量的已知技术确保永远不会重新分配向量。提前一个足够大的 reserve()
应该可以解决问题。
此外,有人指出 i
也是通过引用捕获的,因此在每个新执行线程启动时,它的值可以是任何值。
除了Sam提到的vector同步问题,还有一个问题
这一行:
threads[i] = std::thread([&]() { W[i]->f(); } );
通过引用捕获 i
。 i
很有可能在线程启动 运行 之前超出范围(并被销毁)。语句 W[i]->f();
可能会读取 i
的无效值,该值是负数或太大。请注意,在 i
超出范围之前,最后写入的值是 nw
,因此即使之前包含 i
的内存仍然可以访问,它也可能具有该值nw
太大了。
您可以通过按值捕获 i
来解决 这个 问题:
threads[i] = std::thread([&W, i]() { W[i]->f(); } );
// ^^^^^
// captures W by reference, and i by value
正如其他人所说,捕获是问题所在。
我已将 i
参数添加到 f()
调用中:
void f(int i){
std::vector<int> a(20);
std::iota(a.begin(), a.end(), 1);
auto b = new std::vector<int>(a);
std::cout << "Test 1 " << i << std::endl;
v = b;
std::cout << "Test 2 " << v->size() << std::endl;
}
和输出:Test 1 1
对 f
的调用虽然有效,但它在没有有效 Worker
实例的情况下被调用,当您分配给 v
时,它肯定是在错误的内存中。
当我 运行 以下并行代码时,我在第 18 行(两次打印之间)的分配处遇到分段错误。我不太明白是什么原因造成的。
这是描述问题的最小工作示例:
#include <iostream>
#include <numeric>
#include <vector>
#include <thread>
struct Worker{
std::vector<int>* v;
void f(){
std::vector<int> a(20);
std::iota(a.begin(), a.end(), 1);
auto b = new std::vector<int>(a);
std::cout << "Test 1" << std::endl;
v = b;
std::cout << "Test 2" << std::endl;
}
};
int main(int argc, char** argv) {
int nw = 1;
std::vector<std::thread> threads(nw);
std::vector<std::unique_ptr<Worker>> W;
for(int i = 0; i < nw; i++){
W.push_back(std::make_unique<Worker>());
threads[i] = std::thread([&]() { W[i]->f(); } );
// Pinning threads to cores
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(i, &cpuset);
pthread_setaffinity_np(threads[i].native_handle(), sizeof(cpu_set_t), &cpuset);
}
for (int i = 0; i < nw; i++) {
threads[i].join();
std::cout << (*(W[i]->v))[0] << std::endl;
}
}
似乎用 -fsanitize=address 编译它,代码工作正常,但我的性能最差。我怎样才能让它发挥作用?
std::vector
不是线程安全的。 None C++ 库中的容器是线程安全的。
threads[i] = std::thread([&]() { W[i]->f(); } );
新的执行线程通过引用捕获向量并访问它。
W.push_back(std::make_unique<Worker>());
原始执行线程不断修改此处的向量,而没有与任何新执行线程同步对W
向量的访问。任何 push_back
都可能使 vector 的现有内容无效以便重新分配它,并且如果不同的执行线程试图同时获取 W[i]
,在它被重新分配时,就会出现 hillarity。
这是未定义的行为。
您必须要么使用互斥锁同步对向量的访问,要么使用任意数量的已知技术确保永远不会重新分配向量。提前一个足够大的 reserve()
应该可以解决问题。
此外,有人指出 i
也是通过引用捕获的,因此在每个新执行线程启动时,它的值可以是任何值。
除了Sam提到的vector同步问题,还有一个问题
这一行:
threads[i] = std::thread([&]() { W[i]->f(); } );
通过引用捕获 i
。 i
很有可能在线程启动 运行 之前超出范围(并被销毁)。语句 W[i]->f();
可能会读取 i
的无效值,该值是负数或太大。请注意,在 i
超出范围之前,最后写入的值是 nw
,因此即使之前包含 i
的内存仍然可以访问,它也可能具有该值nw
太大了。
您可以通过按值捕获 i
来解决 这个 问题:
threads[i] = std::thread([&W, i]() { W[i]->f(); } );
// ^^^^^
// captures W by reference, and i by value
正如其他人所说,捕获是问题所在。
我已将 i
参数添加到 f()
调用中:
void f(int i){
std::vector<int> a(20);
std::iota(a.begin(), a.end(), 1);
auto b = new std::vector<int>(a);
std::cout << "Test 1 " << i << std::endl;
v = b;
std::cout << "Test 2 " << v->size() << std::endl;
}
和输出:Test 1 1
对 f
的调用虽然有效,但它在没有有效 Worker
实例的情况下被调用,当您分配给 v
时,它肯定是在错误的内存中。