提问者:小点点

给指针赋值时分段错误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编译它,代码运行良好,但我得到了最差的性能。 我怎么才能让它工作呢?


共3个答案

匿名用户

std::vector不是线程安全的。 C++库中没有一个容器是线程安全的。

threads[i] = std::thread([&]() { W[i]->f(); } );

新的执行线程通过引用捕获向量并访问它。

W.push_back(std::make_unique<Worker>());

原始执行线程在这里不断修改向量,而不与任何新执行线程同步对w向量的访问。 任何push_back都可能会使向量的现有内容无效,以便重新分配它,如果在重新分配它的同时,另一个执行线程尝试获取w[i],那么就会产生hillarity。

这是未定义的行为。

您必须使用互斥体来同步对向量的访问,或者使用任何已知的技术来确保向量永远不会被重新分配。 预先设置一个足够大的reserve()应该可以解决这个问题。

另外,已经指出,i也是通过引用捕获的,因此当每个新的执行线程启动时,它的值可以是任何值。

匿名用户

除了Sam提到的向量同步问题,还有一个问题。

这一行:

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;
    }

和输出:test1

f的调用仍然有效,但是调用它时没有有效的worker实例,并且当您分配给v时,它肯定位于错误的内存中。

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(指针|赋值|分段|c++)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?