我在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;
那里到底发生了什么?上面代码中的每个块变量都有自己的矩阵数据副本吗?或者他们只是通过让指针指向内存中的相同位置来共享它,这意味着matA,matB和matC的行为类似于静态变量(如面向对象编程)?换句话说,是否只有matA,matB和matC的一个副本;并且线程同时访问共享数据?或者有很多副本的副本,并且每个副本在RAM中都有自己不同的分配?
与我的第一篇文章相同的问题,这些背后发生了什么? (* z).arrA = arrayA; (* z).arrB = arrayB; (* z).arrC = arrayC;
以上代码是否足以完成任务(数组加法和矩阵乘法)?或者从内存分配的角度来看,还有另一种更有效的方法吗?
@ Code-Guru:我已经发布了这个新问题。
答案 0 :(得分:3)
线程编程的基本问题是确保两个单独的线程不可能同时修改数据,或者读取另一个线程可能正在修改的数据。 (如果没有数据可能被修改的危险,两个线程同时读取相同的非变化数据是完全可以的。)
应用于矩阵乘法问题,线程只会读取矩阵A和矩阵B,因此您不必控制对这些变量的访问 - 假设它们在启动线程之前已初始化。
另一方面,结果Matrix C将被访问以进行写入,因此您必须确保已对工作负载进行了分区,以便没有两个线程可以访问相同的元素(它们正在处理不相交Matrix C的子集,或者您必须协调访问,以便在给定时间只有一个线程正在修改给定的单元格,并且您已通过互斥(mutex
)或等效的方式强制执行此操作。
除了其他方面,您还没有展示block
的定义方式。如果您向我们展示SSCCE(Short, Self-Contained, Correct Example)会很有帮助,就像我在下面给您看到的那样。它节省了我们不得不将代码片段反向工程为工作代码。做得好,它不占用太多空间。 (这个答案的早期版本因为代码不完整而切断了!)
在您的原始文件中,您创建了NUM_THREAD
个帖子来处理SIZE
x SIZE
矩阵。由于您没有显示SIZE
或NUM_THREAD
的定义,因此我们必须假设两种尺寸相同。根据两个常数的相对大小,可以使用各种不同的灾难配方。
线程都被赋予相同的矩阵来处理,这正是你真正要问的。每个线程都有一个指向同一内存的指针。
假设您引用的(*z).arrA = arrayA;
是(*block).arrA = matriksA;
赋值,那么您将指向SIZE整数数组的指针指向block->arrA
(相当于(*block).arrA
)。这有点扭曲,但合法。你需要小心使用它。
您询问代码是否足够有效。第一个子问题:它是否产生正确的答案(并且有保证)?我还不确定。但是,如果每个线程正在处理结果矩阵的一列,那应该足够安全。
此代码使用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步产生了错误的答案,你知道你只有基本的计算来修复;它不是一个线程引发的问题(因为只有一个线程!)。幸运的是,它产生了正确的答案。添加线程非常简单。
输出
$ ./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
$