Несколько потоков C не возвращают правильные значения - PullRequest
0 голосов
/ 22 ноября 2018

Я пытаюсь умножить две матрицы, используя разные потоки для каждого члена результирующей матрицы.У меня есть этот код:

struct data{
    int p;
    int linie[20];
    int coloana[20];
};

void *func(void *args){

    struct data *st = (struct data *) args;
    int c = 0;

    for(int k = 0; k < st->p; k++){
        c += st->linie[k] * st->coloana[k];
    }

    char *rez = (char*) malloc(5);
    sprintf(rez, "%d", c);

    return rez;
}

int main(int argc, char *argv[]){

    int n = 2;
    int m = 2;

    int A[2][2] = {{1, 2},  
                   {4, 5}};

    int B[2][2] = {{7, 3},  
                  {7, 5}};

    int C[n][m];

    char *res[n * m];
    char *rez[n * m];

    pthread_t threads[n * m];
    int count = 0;

    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            struct data st;
            st.p = 2;
            for(int x = 0; x < st.p; x++){
                st.linie[x] = A[i][x];
                st.coloana[x] = B[x][j]; 
            }

            pthread_create(&threads[count], NULL, func, &st);   

            count++;
        }   
    }

    for(int i = 0; i < n * m; i++){
        pthread_join(threads[i], (void**) &rez[i]);
        printf("%d ", atoi(rez[i]));

    }

    return 0;
}

Но правильный результат никогда не помещается в рез [i].Например, я получаю вывод «63 37 37 37».Код работает отлично, если я не решу дождаться завершения каждого потока, то есть я поместил этот pthread_join сразу после pthread_create во вложенный цикл for.Что я должен делать?Спасибо за чтение!

1 Ответ

0 голосов
/ 22 ноября 2018

Ваша первая проблема с потоками здесь:

for(int i = 0; i < n; i++){
    for(int j = 0; j < m; j++){
        struct data st;
        st.p = 2;
        for(int x = 0; x < st.p; x++){
            st.linie[x] = A[i][x];
            st.coloana[x] = B[x][j]; 
        }
        pthread_create(&threads[count], NULL, func, &st);   
        count++;
    }   
}

Все потоки получают указатель на одну и ту же переменную &st, которая выходит из области действия после каждого вызова pthread_create().Вы должны убедиться, что каждый поток получает свою собственную переменную, и что эта переменная длится до тех пор, пока поток не выйдет.

Чтобы исправить это, например, вы можете попробовать:

struct data st[n * m];

for (int i = 0; i < n; i++)
{
    for (int j = 0; j < m; j++)
    {
        st[count].p = 2;
        for (int x = 0; x < st[count].p; x++)
        {
            st[count].linie[x] = A[i][x];
            st[count].coloana[x] = B[x][j]; 
        }
        int rc = pthread_create(&threads[count], NULL, func, &st[count]);
        if (rc != 0)
            …report pthread creation error…  
        count++;
    }   
}

Это даеткаждый поток имеет свой собственный struct data для работы, и структура переживает цикл pthread_join().

Я не совсем уверен, что это хорошая схема - сделать одну копию соответствующих частей двух массивов.для каждой темы.Это не слишком больно в размере 2x2, но в 20x20, это начинает быть болезненным.Потокам следует указать, какую строку и столбец обрабатывать, а также указать указатели на исходные матрицы и т. Д.Пока никакой поток не изменяет исходные матрицы, проблем с чтением данных не возникает.


Обновленный ответ, который заменяет предыдущий недействительный код, связанный с pthread_join() (как отмечено oftigus в комментарии ) с этим рабочим кодом.Есть причина, которую я обычно проверяю перед публикацией!

В целом, в цикле pthread_join() следует избегать приведений типа (void **).Один правильный рабочий способ справиться с этим:

    for (int i = 0; i < n * m; i++)
    {
        void *vp;
        int rc = pthread_join(threads[i], &vp);
        if (rc == 0 && vp != NULL)
        {
            rez[i] = vp;
            printf("(%s) %d ", rez[i], atoi(rez[i]));
            free(rez[i]);
        }
    }
    putchar('\n');

Передает указатель на переменную void * в pthread_join().Если он находит информацию для запрошенного потока, то pthread_join() заставляет эту переменную void * содержать значение, возвращаемое функцией потока.Затем это можно использовать, как показано - обратите внимание на обработку ошибок (хотя я отмечаю, что пример в спецификации POSIX для pthread_join() игнорирует возвращаемое значение из pthread_join() с приведением (void) к результату).


Я не вижу, где вы используете res или C.


Результат, который я получаю:

(21) 21 (13) 13 (63) 63 (37) 37 

где значение в скобках является строкой, а значение снаружи преобразуется в atoi().Это похоже на правильный ответ для умножения A на B (в таком порядке).

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

struct data
{
    int p;
    int linie[20];
    int coloana[20];
};

static void *func(void *args)
{
    struct data *st = (struct data *)args;
    int c = 0;

    for (int k = 0; k < st->p; k++)
    {
        c += st->linie[k] * st->coloana[k];
    }

    char *rez = (char *)malloc(5);
    sprintf(rez, "%d", c);

    return rez;
}

int main(void)
{
    int n = 2;
    int m = 2;
    int A[2][2] = {{1, 2},  {4, 5}};
    int B[2][2] = {{7, 3},  {7, 5}};
    char *rez[n * m];

    pthread_t threads[n * m];
    int count = 0;
    struct data st[n * m];

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            st[count].p = 2;
            for (int x = 0; x < st[count].p; x++)
            {
                st[count].linie[x] = A[i][x];
                st[count].coloana[x] = B[x][j];
            }
            int rc = pthread_create(&threads[count], NULL, func, &st[count]);
            if (rc != 0)
            {
                fprintf(stderr, "Failed to create thread %d for cell C[%d][%d]\n", count, i, j);
                exit(1);
            }
            count++;
        }
    }

    for (int i = 0; i < n * m; i++)
    {
        void *vp;
        int rc = pthread_join(threads[i], &vp);
        if (rc == 0 && vp != NULL)
        {
            rez[i] = vp;
            printf("(%s) %d ", rez[i], atoi(rez[i]));
            free(rez[i]);
        }
    }
    putchar('\n');

    return 0;
}
...