多个C线程未返回正确的值

时间:2018-11-22 00:51:16

标签: c multithreading pthreads

我正在尝试对所得矩阵的每个成员使用不同的线程来将两个矩阵相乘。我有以下代码:

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

但是正确的结果永远不会放入rez [i]中。例如,我得到输出“ 63 37 37 37”。 如果我不选择等待每个线程完成,则代码可以完美工作,即我将pthread_join放在嵌套的for循环中的pthread_create之后。我该怎么办? 感谢您的阅读!

1 个答案:

答案 0 :(得分:1)

您的第一个线程问题在这里:

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中的comment所指出)。我发布前通常会进行测试是有原因的!

总体上,应该在(void **)循环中避免使用类似pthread_join()的强制类型转换。解决此问题的一种正确方法是:

    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 *的{​​{1}}变量的指针传递给pthread_join()。如果它找到了所请求线程的信息,则pthread_join()会使void *变量保存线程函数返回的值。然后可以按如下所示使用它-注意错误处理(尽管我注意到POSIX规范中针对pthread_join()的示例忽略了pthread_join()的返回值,并在结果上使用了(void) )。


我看不到您在哪里使用resC


我得到的结果是:

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