Почему я не могу получить доступ к массиву, выделенному внутри потока после того, как этот поток завершил выполнение? - PullRequest
0 голосов
/ 06 апреля 2019

Я использую 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 Ответ

4 голосов
/ 06 апреля 2019

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;

Тогда, поскольку vector является указателем на то, где будет находиться int *, а не на int *, мы меняем vector на *vector в следующем коде:

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

Другим способом предоставления int * основному потоку будет возвращение его в качестве возвращаемого значения pthreadWithMalloc. Я использовал метод параметров только для иллюстрации различных типов параметров.

...