Cuenta
Cuenta

Reputation: 57

C++ Multithreading, getting segfault while having multiple threads in parallel

I have a problem while using multiple threads in this code (this is a fraction of it):

template <typename Heuristic, typename Meta>
vector<double> kfold(Heuristic &heu,Meta &metaheu,mat &data,Col<int> &results,int k,int knearest,double pNeigh,double pCost,double pIni,bool st,bool units){

//Some stuff...

for (int j=0; j<k;j++){

        mat auxMat = folds.slice(j);
        Col<int> auxCol = res.col(j);

        Instance auxI(auxU,pNeigh,pCost,&auxMat,&auxMat,&auxCol,&auxCol,un.n_rows);

        threads1[j] = thread(searchInstanceStrat<Heuristic,Meta>,heu,metaheu,auxI,knearest,ref(tiempo),ref(reduction),j);

    }

for (int b = 0; b<k; b++) threads1[b].join();

More stuff to do...  

}

the function called with thread is:

template <typename Heuristic, typename Meta>
void searchInstanceStrat(Heuristic heu,Meta metaheu, Instance auxI, int knearest, vec &tiempo, vector<Instance> &reduction,int index){

auto start = chrono::high_resolution_clock::now();
pair<double,Instance> pairAux = heu.find(auxI,knearest);
//pair<double,Instance> pairAux = metaheu.find(pairAux2.second,knearest);

auto stop = chrono::high_resolution_clock::now();
using fpSeconds = chrono::duration<float,chrono::seconds::period>;
tiempo(index) = (double)(fpSeconds(stop - start).count());
reduction[index] = pairAux.second;
}

The heuristic class is:

template<typename MetricType>
struct CNN{

/*Perform CNN, constructs a reduced data set beginning from one instance and adding each instance not 
 classified correctly */ 

MetricType* metric;
CNN(MetricType* met):metric(met){}

pair<double,Instance> find(Instance &initial, int knearest){

    bool flag = false, flag2 = false;
    Instance current = initial;
    int j = 0;

    vector <int> indexes(initial.units.n_rows);
    for (int i=0;i<initial.units.n_rows;i++) indexes[i] = i;
    random_shuffle(indexes.begin(),indexes.end());

    for (int i = 0; (i < initial.units.n_rows) && !flag; i++){

        Knn knn(current.training,current.trainResults,current.unique);
        flag2 = false;

        while ((j < current.originalTraining->n_rows) && !flag2){

            mat query(current.originalTraining->row(indexes[j]));
            Col<int> prediction(knn.search(query,knearest,*metric));

            if (prediction(0) != (*(current.originaltrainResults))(indexes[j])){
                Col<int> nunits(current.units);
                nunits(indexes[j]) = 1;
                current.units = nunits;
                current.changeTrainingSet();
                flag2 = true;
            }
            j++;

        }
        if (!flag2) flag = true; //flag becomes true when every point is classified correctly
    }

    Knn knn(current.training,current.trainResults,current.unique);
    double costResult = knn.score(*(current.originalTraining),knearest,*metric,*(current.originaltrainResults));

    return make_pair(costResult,current);
}
};

The instance class is:

struct Instance{
Col <int> units;
double percenVecinity, percenCost;
mat* originalTraining;
mat* test;
Col<int>* originaltrainResults;
Col<int>* testResults;
mat training;
Col<int> trainResults;
int unique,totalInstances;

Instance(){}

Instance(Col<int> &u,double p1,double p2,mat* tr,mat* te,Col<int>* trr,Col<int>* ter,int un):
    units(u),percenVecinity(p1),percenCost(p2),test(te),testResults(ter),unique(un),
    originalTraining(tr),originaltrainResults(trr){

        totalInstances = tr->n_rows;
        int count = 0,index=0;
        for (int i=0;i<u.n_rows;i++){ if (u(i)==1) count++; }
        training.set_size(count,tr->n_cols);
        trainResults.set_size(count);

        for (int i=0;i<u.n_rows;i++){
            if (u(i)==1){
                training.row(index) = tr->row(i);
                trainResults(index) = (*trr)(i);
                index++;
            }
        }
    }
}

There is also the Knn class and the metric class but I don't think they are important to the case.

The thing is when I call kfold and create the k threads so each one computes its thing in parallel there is a segmentation fault and it happens when in searchInstanceStrat the find function is called

pair<double,Instance> pairAux = heu.find(auxI,knearest);

But when I put the threads[j].join() after every thread creation (effectively making it serialized) The code works perfectly. The problem is then the concurrency. But I don't understand why, should not the thread() initialization copy every parameter not in ref so each thread has a working copy of the data passed to it?

It is true that the classes have pointers to other data, but if it is a copy, could it be the thing affecting my code? But if there is a copy per thread I don't see why my code is bad.

Thanks in advance for the help

Upvotes: 2

Views: 77

Answers (1)

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32732

The problem is with auxMat and auxCol. Pointers to these local variables are passed to your thread function using the Instance class. These locals are destroyed at the end of the j loop, right after the thread is created. When the thread tries to access them it could be accessing destroyed objects, partially constructed ones for the next thread, or objects that have been constructed for another thread.

You'll want to store the auxMat and auxCol values in Instance, or possibly they could be const pointers into the original data in folds and res.

Upvotes: 2

Related Questions