Приведение пустого указателя - PullRequest
0 голосов
/ 15 октября 2018

Я работаю над заданием, которое требует от меня использования потоков для обработки и синхронизации выборки данных из файла.Мой профессор сказал мне, что я могу изменить свои данные на пустой указатель, чтобы передать их в мою функцию и затем вернуть обратно.Я пытаюсь сделать это с файлом IO.

pthread_create(&th1, NULL, processing, (void *)&fp);

В моей функции обработки я пытаюсь привести его обратно к указателю FILE с помощью:

FILE driveOne = (FILE *)file;

Это явно не работает, так что кто-то может объяснить это мне

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Вот более полный пример.

Допустим, вашей рабочей функции нужен дескриптор файла.Для простоты предположим, что он читает каждый символ из него и возвращает количество прочитанных символов, приведенных к указателю:

void *worker(void *data)
{
    FILE      *handle = (FILE *)data;
    uintptr_t  count = 0;

    if (handle && !ferror(handle)) {
        /* handle is a valid file handle */

        while (getc(handle) != EOF)
            count++;
    }

    return (void *)count;
}

Если count был другого типа, чем intptr_t или uintptr_t (объявлено в <stdint.h>, которое обычно включается включением <inttypes.h>), вам необходимо сначала привести его к этому типу, а затем аннулировать указатель, т. е. (void *)(uintptr_t)count.

, поскольку такиерабочим потокам не требуется много стека (точнее, почти ни одного), а размеры стека потоков по умолчанию огромны (мегабайты), мы можем сэкономить некоторую память (и, при необходимости, разрешить гораздо больше потоков, особенно на 32-разрядных архитектурах),создание атрибута pthread, который инструктирует pthread_create() использовать меньший стек.Этот атрибут не «потребляется» вызовом;это больше похоже на блок конфигурации.

Допустим, у вас есть три потока FILE *in[3];, и вы хотите использовать три потока для проверки их длины.Использование атрибута pthread для использования меньшего стека (2*PTHREAD_STACK_MIN, как определено в <limits.h>, является хорошим, безопасным значением для рабочих потоков, которые не используют alloca() или локальные массивы.):

pthread_t       worker_id[3];
uintptr_t       length[3];
pthread_attr_t  attrs;
void           *retptr;
int             i, result;

/* Create a pthread attribute set, defining smaller stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);

/* Create the three worker threads. */
for (i = 0; i < 3; i++) {
    result = pthread_create(&(worker_id[i]), &attrs, worker, (void *)in[i]);
    if (result) {
        fprintf(stderr, "Cannot create thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }
}

/* pthread attributes are no longer needed. */
pthread_attr_destroy(&attrs);

/*
  ... This thread can do something else here ...
*/

/* Reap the threads, and collect their return values. */
for (i = 0; i < 3; i++) {
    result = pthread_join(worker_id[i], &retptr);
    if (result) {
        fprintf(stderr, "Cannot reap thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }
    length[i] = (uintptr_t)retptr;
}

for (i = 0; i < 3; i++)
    printf("in[%d] contained %llu chars.\n", i, (unsigned long long)length[i]);

Один и тот же шаблон можно использовать, когда вы хотите передать несколько параметров в функцию потока.Сначала вы создаете структуру для хранения этих параметров и создаете их.Вы можете выделять их динамически, объявлять их как глобальные переменные или объявлять их как локальные переменные в main () - любая область действия, которая существует в течение полной продолжительности, когда существует рабочий поток, работает.

Например, давайтескажем, ваша рабочая функция вычисляет гистограмму каждого значения unsigned char, которое она читает из потока:

struct work {
    pthread_t     id;                   /* Thread identifier */
    FILE         *in;                   /* File handle to read from */
    size_t        count[UCHAR_MAX + 1]; /* Histogram */
};

void *worker(void *data) {
    struct work *const  work = (struct worker_data *)data;
    int                 c;

    if (!work || !work->in) {
        /* Invalid data, or invalid file handle. */
        return (void *)(intptr_t)(EINVAL);
    }
    if (ferror(work->in)) {
        /* Stream is in error state. */
        return (void *)(intptr_t)(EIO);
    }

    /* Read the stream. */
    while ((c = getc(work->in)) != EOF) {
        /* Update histogram. */
        work->count[(unsigned char)c]++;
    }

    /* Did the reading stop due to an I/O error? */
    if (ferror(work->in))
        return (void *)(intptr_t)(EIO);

    /* No errors, all done. */
    return (void *)0;
}

Обратите внимание, что struct work *const work = ... инициализирует постоянный указатель work, а не указатель на константу.const - это просто оптимизация, которая сообщает компилятору C, что мы не будем пытаться модифицировать сам указатель work.Данные, на которые он указывает, являются изменяемыми.

(Чтобы прочитать объявления указателей, прочитайте их справа налево, заменяя каждый * на "указателем на" , чтобы получитьв полном смысле этого слова.)

Код для создания этих рабочих очень похож, за исключением того, что мы распределяем работу динамически:

struct work    *work[3];
pthread_attr_t  attrs;
void           *retptr;
int             i, result;

/* Create and initialize the three pointers. */
for (i = 0; i < 3; i++) {

    /* Allocate a work structure. */
    work[i] = malloc(sizeof *(work[i]));
    if (!work[i]) {
        fprintf(stderr, "Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    /* Copy the handle to read from, */
    work[i]->in = in[i];

    /* and clear the histogram part. */
    memset(work[i]->count, 0, sizeof work[i]->count);
}

/* Create a pthread attribute set, defining smaller stack size. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);

/* Create the three worker threads. */
for (i = 0; i < 3; i++) {
    result = pthread_create(&(work[i]->id), &attrs, worker, (void *)work[i]);
    if (result) {
        fprintf(stderr, "Cannot create thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }
}

/* pthread attributes are no longer needed. */
pthread_attr_destroy(&attrs);

/*
  ... This thread can do something else here ...
*/

/* Reap the threads, and collect their return values. */
for (i = 0; i < 3; i++) {
    result = pthread_join(work[i]->id, &retptr);
    if (result) {
        fprintf(stderr, "Cannot reap thread: %s.\n", strerror(result));
        exit(EXIT_FAILURE);
    }

    /* If the thread reported a failure, print the corresponding
       error message (but do not exit). */
    if (retptr)
        fprintf(stderr, "Thread %d of 3: %s.\n", i+1, strerror((intptr_t)retptr));

    /* ... print the histogram here? ... */
}

/* Free the work structures. */
for (i = 0; i < 3; i++)
    free(work[i]);

Если вы не хотите прерывать программу, когдавозникает ошибка, полезно отметить, что free(NULL) безопасен и ничего не делает;и struct work *pointerarray[SIZE] = {0}; объявляет массив указателей SIZE на struct work и инициализирует их все в ноль.Например, если в какой-то момент происходит сбой выделения или создания потока, вы можете просто free() каждый указатель, независимо от того, было ли его выделение успешным.

То есть, если вы хотите выделить три различных типа структур(struct atype *a;, struct btype *b; и struct ctype *c;), вы можете сделать

a = malloc(sizeof *a);
b = malloc(sizeof *b);
c = malloc(sizeof *c);
if (!a || !b || !c) {
    free(c);
    free(b);
    free(a);
    return ALLOCATION_FAILED;
}

/* Allocation was successful */

вместо того, чтобы выделять каждый и проверять на отказ отдельно.

0 голосов
/ 15 октября 2018

Вам нужно объявить driveOne как FILE *, а не FILE.

FILE *driveOne = (FILE *)file;

Кроме того, предполагая, что fp первоначально было объявлено как FILE *, ваш вызов pthread_create не должно иметь & перед fp, вот так:

pthread_create(&th1, NULL, processing, (void *)fp);
...