Race conditions in threads despite mutexlocking

Viewed 189

This code calculates the sum of all integers in an array, evenly dividing the work between multiple threads. However every once in a while the thread numbers as well as the localsum of the thread are messed up. I am assuming it's because void* param and globalindex are accessed by multiple threads at the same time. What does not make sense is the fact that it happens, even though I mutexlocked every global variable in this code. How do I fix this?

#include<string>
#include<iostream>
#include<fstream>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

using namespace std;
int y =0;
int sum=0;
int array[1000000];
int x=0;
int leftoverHandle = 0;
int globalindex = 0;  
int eachThreadHandles =0;

void* add(void* param){
    pthread_mutex_lock(&mutex);
    int localindexup = globalindex + eachThreadHandles;
    int localindexdown = globalindex;
    int localsum=0;
    long localparam = (long)param;

    if(y != leftoverHandle ){
            localindexup++;
            y++;

    }
    pthread_mutex_unlock(&mutex);


    while(localindexdown<localindexup){

            pthread_mutex_lock(&mutex);
            sum = sum+array[localindexdown];
            localsum = localsum+array[localindexdown];
            localindexdown++;
            pthread_mutex_unlock(&mutex);


    }




    pthread_mutex_lock(&mutex);
    globalindex = localindexdown;
    printf("Thread %ld", localparam);
    printf(": %d\n", localsum);
    pthread_mutex_unlock(&mutex);
}

int main(int argc, char ** argv){
    if(argc != 3){
            cout<<"Incorrect number of argument";
            exit(1);
    }
    string line;
    string f = argv[1];
    const char *filename = f.c_str();



    int maxthreads = atoi(argv[2]);

    FILE* inFile = fopen(filename,"r");
    int i=0;
    if(inFile == NULL){
    cout<<"fopen failed"<<endl;
    }

    fscanf(inFile, "%d",&i);

    while(!feof(inFile)){

    array[x]=i;
    x +=1;

    fscanf(inFile,"%d",&i);

    }

    fclose(inFile);

    pthread_t id[maxthreads];

    leftoverHandle = x%maxthreads;
    eachThreadHandles = (x - leftoverHandle)/maxthreads;


    for(long i=0; i< maxthreads;i++){
            long status = pthread_create(&id[i], NULL, add, (void*) i);
            if(status){
                    printf("Error creating thread! \n");
                    exit(0);
            }
    }

    for(long i=0; i<maxthreads;i++){
    pthread_join(id[i], NULL);
    }


    cout<<"Sum="<<sum<<endl;




return 0;
}
1 Answers

The problem is that you are not updating globalindex right after initializing localindexup and localindexdown per each thread, i.e in the first critical section.
You have three critical sections in your code.
Imagine that thread0 runs the first critical section, then thread1 preempts thread0 right after thread0 releases the lock of the first critical section. But because you are setting globalindex to localindexdown in the third critical section, not the first one, thread1 will still see globalindex=0, just like thread0, so it is going to recalculate the same sum as thread0. You should put globalindex = localindexdown; into the first critical section.
Actually there is no need for the third critical section at all:

    pthread_mutex_lock(&mutex);
    int localindexup = globalindex + eachThreadHandles;
    int localindexdown = globalindex;
    int localsum=0;
    long localparam = (long)param;

    if(y != leftoverHandle ){
            localindexup++;
            y++;

    }
    globalindex = localindexdown; //<--- MOVED HERE
    pthread_mutex_unlock(&mutex);

And forget my comment about the loop, I made a mistake: while(localindexdown<localindexup) can be safely preempted because the variables are not shared among threads. You can just improve the performance a bit by reducing the mutexed region to include only shared data:

    while(localindexdown<localindexup)
    {
            pthread_mutex_lock(&mutex);
            sum = sum+array[localindexdown];
            pthread_mutex_unlock(&mutex);     //<--- MOVED HERE
            localsum = localsum+array[localindexdown];
            localindexdown++;
    }
Related