pthread编程中的共享数据

时间:2013-03-25 01:18:13

标签: c pointers variable-assignment matrix-multiplication

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

我之前的问题在这里: How do I assign array variable in a simple Pthread programming?

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

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。

但我还不确定我在那里做什么。 我是否只是通过分配指针或者什么来将2D数组复制到另一个2D数组? 有点混乱....哈哈......

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

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

  2. 与我的第一篇文章相同的问题,这些背后发生了什么? (* z).arrA = arrayA; (* z).arrB = arrayB; (* z).arrC = arrayC;

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

  4. @ Code-Guru:我已经发布了这个新问题。

1 个答案:

答案 0 :(得分:3)

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

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

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

您的问题

除了其他方面,您还没有展示block的定义方式。如果您向我们展示SSCCE(Short, Self-Contained, Correct Example)会很有帮助,就像我在下面给您看到的那样。它节省了我们不得不将代码片段反向工程为工作代码。做得好,它不占用太多空间。 (这个答案的早期版本因为代码不完整而切断了!)

在您的原始文件中,您创建了NUM_THREAD个帖子来处理SIZE x SIZE矩阵。由于您没有显示SIZENUM_THREAD的定义,因此我们必须假设两种尺寸相同。根据两个常数的相对大小,可以使用各种不同的灾难配方。

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

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

  3. 您询问代码是否足够有效。第一个子问题:它是否产生正确的答案(并且有保证)?我还不确定。但是,如果每个线程正在处理结果矩阵的一列,那应该足够安全。

  4. 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. 添加线程。
    3. 如果第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
      $