为什么在该线程执行完后为什么不能访问线程内部分配的数组?

时间:2019-04-06 12:57:51

标签: c pthreads malloc

我正在使用malloc为数组分配内存。我意识到,如果我在线程内使用malloc且该线程停止执行,则无法访问上述数组。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NUM_THREADS 2
#define N 4

void *threadWithoutMalloc(int *vector)
{
    int i;
    for (i = 0; i < N; i++)
    {
        vector[i] = i + 1;
    }
    return NULL;
}

void *threadWithMalloc(int *vector)
{
    vector = malloc(sizeof(int) * N);
    int i;
    for (i = 0; i < N; i++)
    {
        vector[i] = i + 1;
    }
    return NULL;
}


int main()
{
    //Generic stuff
    pthread_t *threads;
    threads = malloc(NUM_THREADS * sizeof(pthread_t));
    int rc;
    long t;
    int **pointer_vector = malloc(N * sizeof(int *));

    //Allocating the vector before entering the thread
    pointer_vector[0] = malloc(sizeof(int)*N);

    rc = pthread_create(&threads[0], NULL, (void *)threadWithoutMalloc, pointer_vector[0]);

    if (rc)
    {
        printf("Error! Code %d\n", rc);
    }

    //Allocating the vector inside the thread
    rc = pthread_create(&threads[1], NULL, (void *)threadWithMalloc, pointer_vector[1]);

    if (rc)
    {
        printf("Error! Code %d\n", rc);
    }

    //Waiting for the threads to finish executing
    pthread_join(threads[0], NULL);
    pthread_join(threads[1], NULL);

    //This works
    printf("%d\n", pointer_vector[0][0]);

    //This results in a segmentation fault
    printf("%d\n", pointer_vector[1][0]);

    return 0;
}

为什么会这样?我目前的假设是,线程运行过程后,其内存将被释放。但是,我正在使用动态分配,并将结果存储在main()上声明的变量中。我只想了解发生了什么事。

1 个答案:

答案 0 :(得分:4)

threadWithMalloc永远不会将分配的存储的地址发送回其调用方或主线程。它使用参数vector声明,然后为vector分配一个值:

vector = malloc(sizeof(int) * N);

所有这些操作就是更改参数的值,该值实际上是该函数的局部值。更改函数中的参数不会更改在主例程中传递的参数。因此,在主例程中pointer_vector[1]不变。

要解决此问题,让我们首先修复例程声明。 pthread_create采用类型为void *(*)(void *)的参数,该参数是指向一个例程的指针,该例程采用void *参数并返回void *结果。因此,线程例程应该声明为带有void *参数并返回void *结果的例程,例如:

void *threadWithoutMalloc(void *parameter)

现在,在例程中,threadWithoutMalloc想要一个int *,而不是void *。我们可以通过赋值来满足这一要求:

int *vector = parameter;

然后,当我们为threadWithoutMalloc创建线程时,无需铸造指针就可以做到:

rc = pthread_create(&threads[0], NULL, threadWithoutMalloc, pointer_vector[0]);

您的编译器应该向您发出有关pthread_create代码的警告-将例程投射到void *是一个不好的信号(并且其行为未由C标准定义),并且结果void *还必须由编译器转换为参数类型void *(*)(void *),该参数类型也具有C标准未定义的行为,并且违反了约束。如果您的编译器没有为此提供警告,则应在编译器中启用更多警告。

对于threadWithoutMalloc,上面的代码将其传递给int *,这对于只接收int *的东西是很好的。对于threadWithMalloc,我们希望它为我们提供int *。一种实现方法是向其传递一个指向int *的指针,该指针为其赋予了我们希望其存储int *的空间地址。为此,我们可以向其传递pointer_vector[1]的地址:

rc = pthread_create(&threads[1], NULL, threadWithMalloc, &pointer_vector[1]);

然后,threadWithMalloc我们再次要修改其声明:

void *threadWithMalloc(void *parameter)

并将参数分配给所需类型的对象:

int **vector = parameter;

然后,由于vectorint *本身的指针,而不是int *本身,因此我们将vector更改为*vector以下代码:

(*vector) = malloc(sizeof(int) * N);
int i;
for (i = 0; i < N; i++)
{
    (*vector)[i] = i + 1;
}

向主线程提供int *的另一种方法是将其作为pthreadWithMalloc的返回值返回。我仅使用参数方法来说明不同类型的参数。