将动态分配的指针传递给pthread的正确方法是什么

时间:2019-04-06 20:02:43

标签: c multithreading pointers pthreads

我正在编写一个程序,用于使用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。我在上下搜索了一些解释,却一无所获。我在做什么错了?

1 个答案:

答案 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 *;否则内存将被泄漏。