【问题标题】:Shared Data in pthread Programmingpthread 编程中的共享数据
【发布时间】:2013-03-14 10:16:36
【问题描述】:

在 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。

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

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

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

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

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

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

【问题讨论】:

  • (*block).matA 等价于block-&gt;matA。您可以使用 SIMD 指令更有效地执行此操作。特殊用途的矩阵有很多优化,比如备用或对称。一般人必须做该做的工作。您可以将乘法划分为单独的线程或直接到机器级别的代码。许多机器都有乘法和累加指令。一个体面的编译器会识别这一点并将其与您拥有的代码一起使用。唯一的另一个技巧是启动循环以获得最佳内存访问。
  • 对不起,那是 sparse 矩阵。通过这个stack overflow question可以看到,你的方法已经很不错了。
  • 您在顶部的代码确实是正确的声明。可以肯定的是,它们是指针。像所有非空指针一样,它们是有类型的。在这种情况下,它们被键入为“指向 SIZE 整数数组的指针”。您是正确的,第二个 decl(您尝试但没有成功的那个)不正确。这些不是指向数组的指针;它们是指向指针的指针,通常用于模拟不连续的多维数组分配。我在您的代码中看到的最直接的问题是block 的递增。由于没有decl,我无法判断是否有N个,每个线程一个。

标签: c pointers variable-assignment matrix-multiplication


【解决方案1】:

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

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

另一方面,结果矩阵 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-&gt;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-11-21
    • 2021-02-21
    • 2021-06-22
    • 2017-02-10
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多