Soul Bruteflow
Soul Bruteflow

Reputation: 115

Writing to one array from multiple threads

I am learning pthreads.

Right now I am trying to make the program that writes to one 2d array using multiple pthreads. Each pthread is responsible for only one line of the array. So there is no race or overlap there. The goal is to make it as fast as possible without using global variables.

The first solution that I implemented was the one that uses a global variable. And it works as intended. Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int **array;
const int NTHREADS = 5;
const int ELEMENTS = 3;

void *worker(void *arg);
void print_array(int **array);

int main()
{
    int i, j;
    pthread_t* threads = (pthread_t*)malloc(NTHREADS * sizeof(pthread_t));

    array = (int**)malloc(sizeof(int*));
    for(i = -1; i < NTHREADS; i++)
    {
        array[i] = (int*)malloc(sizeof(int));
        for (j = -1; j < ELEMENTS; j++)
        {
            array[i][j] = (int)malloc(sizeof(int));
        }
    }

    for (i = 0; i < NTHREADS; i++)
        pthread_create(&threads[i], NULL, worker, (void*)i);

    for (i = 0; i < NTHREADS; i++)
        pthread_join(threads[i], NULL);

    print_array(array);
    return 0;
}

void *worker(void *arg)
{
    int tid = (int)arg;

    for (int j = 0; j < ELEMENTS; j++)
        array[tid][j] = j;
    return (NULL);
}

void print_array(int **array)
{
    for (int i = 0; i < NTHREADS; i++)
    {
        for (int j = 0; j < ELEMENTS; j++)
            printf("%d,", array[i][j]);

        printf("\n");
    }
}

Then I wrote the same program using struct instead of global variable. Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

const int NTHREADS = 5;
const int ELEMENTS = 3;

typedef struct          s_asd
{
    int                 **array;
    int                 tid;
}                       t_asd;

void *worker(void *arg);
void print_array(int **array);

int main()
{
    pthread_t* threads = (pthread_t*)malloc(NTHREADS * sizeof(pthread_t));
    t_asd tmp;
    int i, j;

    tmp.array = (int**)malloc(sizeof(int*));
    for (i = 0; i <= NTHREADS; i++)
    {
        tmp.array[i] = (int*)malloc(sizeof(int));
        for (j = 0; j <= ELEMENTS; j++)
            tmp.array[i][j] = (int)malloc(sizeof(int));
    }

    for (tmp.tid = 0; tmp.tid < NTHREADS; tmp.tid++)
        pthread_create(&threads[tmp.tid], NULL, worker, &tmp);

    for (i = 0; i < NTHREADS; i++)
        pthread_join(threads[i], NULL);

    print_array(tmp.array);
    return 0;
}

void *worker(void *arg)
{
    t_asd   *tmp = (t_asd*)arg;

    for (int j = 0; j < ELEMENTS; j++)
        tmp->array[tmp->tid][j] = j;
    return (NULL);
}

void print_array(int **array)
{
    for (int i = 0; i < NTHREADS; i++)
    {
        for (int j = 0; j < ELEMENTS; j++)
            printf("%d,", array[i][j]);

        printf("\n");
    }
}

This one, prints random numbers. I know that I am using the same pointer in all threads, but threads themselves, are not using the same memory area. So why does it prints random numbers? What is the best solution, without using a global variable?

Update 1. Output of the second program:

-1413467520,32668,-1413467440,
-1413467584,-1413467568,-1413467552,
-1413467504,-1413467488,-1413467472,
0,1,2,
0,1,2,

Upvotes: 0

Views: 2261

Answers (2)

ShellCode
ShellCode

Reputation: 1162

Try something like that :

int main()
{
    pthread_t* threads = (pthread_t*)malloc(NTHREADS * sizeof(pthread_t));
    t_asd tmp;
    int i, j;

    tmp.array = (int**)malloc(NTHREADS * sizeof(int*));
    for (i = 0; i <= NTHREADS; i++)
    {
        tmp.array[i] = (int*)malloc(ELEMENTS * sizeof(int));

        //can be deleted if you want
        for (j = 0; j <= ELEMENTS; j++)
            tmp.array[i][j] = 0;
    }

    for (tmp.tid = 0; tmp.tid < NTHREADS; tmp.tid++) {
        t_asd *arg = (t_asd *) malloc(sizeof(t_asd));
        memcpy(arg, &tmp, sizeof(t_asd)); //will copy the current tid and the pointer to the array in a new memory area
        pthread_create(&threads[tmp.tid], NULL, worker, arg);
    }

    for (i = 0; i < NTHREADS; i++)
        pthread_join(threads[i], NULL);

    print_array(tmp.array);
    return 0;
}

Of course this is an example and you have to free all the allocations

Upvotes: 1

Andriy Berestovskyy
Andriy Berestovskyy

Reputation: 8534

You are passing local variable tmp as an argument to the thread and changing it in a loop at the same time. This is a data race and your threads most probably will operate over the same data.

Convert tmp to an array, fill and pass a corresponding element to a corresponding thread.

Upvotes: 1

Related Questions