2

在 pthread 编程中,我仍然不太确定。如果有人能告诉我一个绝对的答案,我将不胜感激。

我之前的问题在这里: 如何在简单的 Pthread 编程中分配数组变量?

现在,我正在研究矩阵乘法。使用它效果很好:

typedef struct {
    int rowIdx;
    int (*matA)[SIZE], (*matB)[SIZE], (*matC)[SIZE];
} newType; 

int main (){
    int matriksA[SIZE][SIZE];
    int matriksB[SIZE][SIZE];
    int matriksC[SIZE][SIZE];

    for (i=0;i<NUM_THREAD;i++) {
         (*block).rowIdx = i;
         (*block).matA = matriksA;
         (*block).matB = matriksB;
         (*block).matC = matriksC;
         pthread_create(&arrThread[i], NULL, matrixMul, (void *)block);
         block++;
    }
}

void *matrixMul(void *x){
    newType *p = (newType *) x;
    int i = (*p).rowIdx;
    int j,k;
    for (j=0;j<SIZE;j++){
        int result = 0;
        for(k=0;k<SIZE;k++){
            int MAik = (*p).matA[i][k];
            int MBkj = (*p).matB[k][j];
            result = result + (MAik*MBkj);
        }
        (*p).matC[i][j] = result;
    }
    pthread_exit(NULL);
}

matrixMul 正在执行矩阵乘法 matC = matA x matB。

我之前尝试过使用这个结构,但它没有用。

typedef struct {
    int rowIdx;
    int **matA, **matB, **matC;
} newType; 

显然,从我读过的内容来看,变量数组可以被认为是一个指针,它保存着数组中第一个元素的地址。至于二维数组,我们必须告诉编译器列的大小。因此,我们必须在 typedef 结构中使用 (*matA)[SIZE] 而不是 **matA。

但我仍然不确定我在那里做什么。我只是通过分配指针将二维数组复制到另一个二维数组还是什么?有点混乱……哈哈……

我的下一个问题是关于这些行:

(*block).matA = matriksA;
(*block).matB = matriksB;
(*block).matC = matriksC;
  1. 那里究竟发生了什么?上面代码中的每个变量是否都有自己的矩阵数据副本?或者他们只是通过让他们的指针指向内存中的相同位置来共享它,这意味着 matA、matB 和 matC 的行为就像一个静态变量(如在面向对象编程中)?也就是说,matA、matB、matC是否只有一份?线程是否同时访问共享数据?或者是否有许多“matA”副本,并且每个副本在 RAM 中都有自己不同的分配?

  2. 我的第一篇文章也有同样的问题,这些线后面发生了什么?(*z).arrA = arrayA; (*z).arrB = arrayB; (*z).arrC = 数组C;

  3. 上面的代码是否足够有效地完成任务(数组加法和矩阵乘法)?或者从内存分配的角度来看,还有另一种更有效的方法吗?

@Code-Guru:我已经发布了新问题。

4

1 回答 1

3

线程编程的基本问题是确保两个单独的线程不可能同时尝试修改数据,或者读取另一个线程可能正在修改的数据。(如果不存在数据被修改的危险,两个线程同时读取相同的不变数据是完全可以的。)

应用于您的矩阵乘法问题,线程只会读取矩阵 A 和矩阵 B,因此您不必控制对这些变量的访问 - 假设它们在您启动线程之前已初始化。

另一方面,结果 Matrix C 将被访问以进行写入,因此您必须确保已对​​工作负载进行了分区,以便没有两个线程将访问相同的元素(它们正在处理 Matrix 的不相交子集C),或者您必须协调访问,以便在给定时间只有一个线程正在修改给定单元格,并且您已经通过互斥 (a mutex) 或等效的东西强制执行此操作。

你的问题

除其他事项外,您还没有展示如何block定义。如果您向我们展示一个 SSCCE(简短、独立、正确的示例),就像我在下面向您展示的那样,这将很有帮助。它使我们不必将您的代码片段反向工程为工作代码。处理得当,它不会占用太多空间。(由于代码不完整,此答案的早期版本偏离了方向!)

在您的原始文件中,您创建了NUM_THREAD线程来处理SIZExSIZE矩阵。由于您没有显示SIZEor的定义NUM_THREAD,我们必须假设两个大小相等。根据两个常数的相对大小,可以使用各种不同的灾难配方。

  1. 线程都被赋予了相同的矩阵来处理,这就是你真正要问的。每个线程都有一个指向同一内存的指针。

  2. 假设(*z).arrA = arrayA;您指的是(*block).arrA = matriksA;分配,那么您正在分配一个指向 SIZE 整数数组的指针block->arrA(相当于(*block).arrA)。这有点扭曲,但合法。你需要小心使用它。

  3. 你问代码是否足够高效。第一个子问题:它是否会产生正确的答案(并且可以保证)?我还不确定。但是,如果每个线程都在结果矩阵的一列上工作,那应该是足够安全的。

SSCCE

此代码使用 C99 构造。它不会在 C89 下编译。

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

enum { SIZE = 3 };

typedef struct
{
    int   rowIdx;
    int (*matA)[SIZE];
    int (*matB)[SIZE];
    int (*matC)[SIZE];
} newType; 

extern void *matrixMul(void *);

static void print_matrix(const char *tag, int d1, int d2, int matrix[d1][d2])
{
    printf("%s: (%d x %d)\n", tag, d1, d2);
    for (int i = 0; i < d1; i++)
    {
        printf("%d:", i);
        for (int j = 0; j < d2; j++)
            printf(" %6d", matrix[i][j]);
        putchar('\n');
    }
}

int main(void)
{
    int matriksA[SIZE][SIZE] = { {  1,  2,  3 }, {  4,  5,  6 }, {  7,  8,  9 } };
    int matriksB[SIZE][SIZE] = { { 11, 12, 13 }, { 14, 15, 16 }, { 17, 18, 19 } };
    int matriksC[SIZE][SIZE];
    newType  thedata[SIZE];
    newType *block = thedata;
    pthread_t arrThread[SIZE];

    for (int i = 0; i < SIZE; i++)
    {
         block->rowIdx = i;
         block->matA = matriksA;
         block->matB = matriksB;
         block->matC = matriksC;
         //matrixMul(block);
         pthread_create(&arrThread[i], NULL, matrixMul, block);
         block++;
    }

    for (int i = 0; i < SIZE; i++)
        pthread_join(arrThread[i], 0);

    print_matrix("Matrix A", SIZE, SIZE, matriksA);
    print_matrix("Matrix B", SIZE, SIZE, matriksB);
    print_matrix("Matrix C", SIZE, SIZE, matriksC);
}

void *matrixMul(void *x){
    newType *p = (newType *) x;
    int i = p->rowIdx;
    for (int j = 0; j < SIZE; j++)
    {
        int result = 0;
        for(int k = 0; k < SIZE; k++)
        {
            int MAik = p->matA[i][k];
            int MBkj = p->matB[k][j];
            result += MAik * MBkj;
        }
        p->matC[i][j] = result;
    }
    //pthread_exit(NULL);
    return(0);
}

您可能会注意到我添加了矩阵打印功能(并使用了它)。我还为一对 3x3 矩阵添加了样本数据,并且我已经验证了答案是正确的。我分两步进行了测试:

  1. 检查代码的单线程版本是否产生了正确的答案。
  2. 添加线程。

如果第 1 步产生了错误的答案,你就知道你只需要修正基本的计算;这不是线程引起的问题(因为只有一个线程!)。幸运的是,它产生了正确的答案。然后添加线程很简单。

输出

$ ./ta
Matrix A: (3 x 3)
0:      1      2      3
1:      4      5      6
2:      7      8      9
Matrix B: (3 x 3)
0:     11     12     13
1:     14     15     16
2:     17     18     19
Matrix C: (3 x 3)
0:     90     96    102
1:    216    231    246
2:    342    366    390
$ 
于 2013-03-25T01:59:00.663 回答