我正在编写一个程序,用于使用pthreads执行矩阵乘法。它通过指定矩阵大小 n (假定矩阵为正方形)和线程数 p (假定将 n < / i>均匀。对于 A x B , A 被水平划分为 p 个段,每个线程将单个段作为输入接收,并且整个矩阵 B ,并返回结果矩阵 C 的一部分。
我遇到的问题实际上与分配本身无关,而是与我无法找到答案的pthread性质有关的一个更普遍的问题。我会尽量将其剥离。我的矩阵以一维数组的形式存储在结构中。
typedef struct matrix {
int *matrix;
int size;
} matrix_t
像这样分配它们
matrix_t mtx = {
malloc(input_size * input_size * sizeof(int)),
input_size
};
并由函数随机填充。分区存储在2D数组中,其地址从函数返回,但按常规方式分配:
int **partitions = partitionmtx(mtx, num_threads);
int **partitionmtx(matrix_t mtx, int threads)
{
int partlen = mtx.size * (mtx.size / threads);
int **parts = malloc(threads * sizeof(int));
for(int i = 0; i < threads; ++i) {
parts[i] = malloc(partlen * sizeof(int));
// partitions populated...
}
return parts;
}
这很好。当我将每个分区发送给一个线程时,就会出现问题。为了使线程的参数简单,我将它们像这样捆绑在一起:
typedef struct operand {
matrix_t matrix;
int *partition;
int partition_length;
} operand_t;
我正在像这样创建pthread:
pthread_t threads[num_threads];
pthread_mutex_init(&mymutex, NULL);
int rc;
for(int i = 0; i < num_threads; ++i) {
operand_t op = {matrix, partitions[i], partition_length};
rc = pthread_create(&threads[i], NULL, partition_product, (void *)&op);
assert(rc == 0);
}
for(int i = 0; i < num_threads; ++i) {
rc = pthread_join(threads[i], NULL);
assert(rc == 0);
}
移至我的函数partition_product。显然,我的首要任务是确保每个线程都获取正确的数据,因此我打印了每个线程的状态:
void* partition_product(void *args)
{
operand_t *op = (operand_t *)args;
pthread_mutex_lock(&mymutex);
printf("Matrix:\n);
printmtx(op->matrix); // This is a function I defined but its details aren't relevant here
printf("\nPartition:" );
for(int i = 0; i < op->partition_length; ++i)
printf("%4d", op->partition[i]);
pthread_mutex_unlock(&mymutex);
}
这是我的问题所在。矩阵从线程中毫无问题地打印出来。问题是,一旦我指定了多个线程,例如
,所有线程./threadmatrix -n 4 -p 4
全部打印相同的分区。我认为这可能是从线程打印的副作用,因此互斥锁在打印件上。我想然后在原始线程和创建的线程中打印每个分区的地址[i],以查看正在发生的情况,而且似乎每个线程从创建时开始都接收相同的地址。我正在将数据输入线程,并且似乎能够毫无问题地对其进行操作,但是它们都是相同的数据。具体来说,它们总是获取最后一个分区的地址。我已经尝试了所有我知道的良好指针实践,但是如果partitions [i]的地址为0x00007ffffde234,那么上述调用中的所有4个线程都会打印地址0x00007ffffde234。我在上下搜索了一些解释,却一无所获。我在做什么错了?
答案 0 :(得分:3)
您的问题在这里:
operand_t op = {matrix, partitions[i], partition_length};
rc = pthread_create(&threads[i], NULL, partition_product, (void *)&op);
请注意,您在最后一个参数中传递的指针是指向op
的指针,该指针位于堆栈上。这样做的问题是,一旦主线程完成了其for循环的迭代,op
将被销毁,然后为循环的下一个迭代重新创建。这意味着以后,当子线程开始运行并尝试使用该指针参数时,指针指向的operand_t
将不再有效。 (在您的情况下,所有创建的所有子线程都将重复使用相同的堆栈内存位置,这部分解释了您所看到的行为)
为避免该问题,您需要确保传递指针的对象的生存期足够长,以使得当子线程取消引用指针以读取对象的字段时,该对象仍然有效。最简单的方法是在堆上分配对象,
operand_t * op = (operand_t *) malloc(sizeof(operand_t));
op->matrix = matrix;
op->partition = partitions[i];
op->partition_length = partition_length;
rc = pthread_create(&threads[i], NULL, partition_product, (void *)op);
唯一的(小)问题是,使用该对象完成操作后,您的子线程现在将负责在收到的free
上调用operand_t *
;否则内存将被泄漏。