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

标签 c multithreading pointers pthreads

我正在编写一个程序,用于使用 pthread 执行矩阵乘法的分配。它的工作原理是指定矩阵大小 n(假定矩阵是方阵)和线程数 p(假定除以 n 均匀。对于 A x BA 被水平划分为 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
};

并由函数随机填充。分区存储在一个二维数组中,其地址从函数返回,但按正常方式分配:

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。我到处寻找一些解释,但一无所获。我做错了什么?

最佳答案

您的问题在这里:

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);

唯一(小)的问题是,在使用该对象完成之后,您的子线程现在将负责在其接收的 operand_t * 上调用 free ;否则内存会泄漏。

关于c - 将动态分配的指针传递给 pthread 的正确方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55552990/

相关文章:

multithreading - Gstreamer缓冲区与CUDA转换

C++ 指针 - 冲突声明和指针到指针

c - 模块化编译时数组扩展

c - 如何使用指针打印 C 函数的结果(不返回)

c - int num 中至少一位为 0

android - 线程能否安全调用 TCriticalSection.Acquire 多次?

java - 当新的多线程消费者添加到组中时是否会发生重新平衡

c - 如何在 C 中使用 [] 声明指向 const 字符数组的 Const 指针

c - Linux 和 C : Communicating with X server from outside of X?

c - 未在 C 中正确释放线程