Мне трудно работать над структурой, используя несколько файлов.Пожалуйста помоги? - PullRequest
1 голос
/ 07 ноября 2011

Я пытаюсь решить проблему потребителя / производителя, и мне нужно создать три разных класса.

Основной класс включает создание потоков и логику потребителя / производителя

два других класса

  • Заголовочный файл для кольцевого буфера
  • Файл, содержащий реализацию кольцевого буфера

Я получаю следующие ошибки при попытке компиляции:

ringbuf.c: In function ‘rb_init’:
ringbuf.c:10: warning: incompatible implicit declaration of built-in function ‘malloc’
ringbuf.c:10: error: invalid application of ‘sizeof’ to incomplete type ‘struct ringbuf_t’ 
ringbuf.c:12: error: dereferencing pointer to incomplete type

У меня много других ошибок, но я смогу справиться с ними сам, как только преодолею это.

это заголовочный файл для буфера:

struct ringbuf_t 
{
    pthread_mutex_t mutex; /* lock to protect from simutaneous access to the buffer */
    pthread_cond_t cond_full; /* producer needs to wait when the buffer is full */
    pthread_cond_t cond_empty; /* consumer needs to wait when the buffer is empty */
    int bufsiz; /* size of the buffer; you may use an empty slot to differentiate the situation the buffer is full or empty */
    int front; /* index of the first element */
    int back; /* index next to the last element (or index to the first empty slot) */
    int count; //keeps track of the number of elements in the buffer
    char* buf; /* buffer itself */
};

/* return the pointer to the newl created and initialized ring buffer of the given size */
extern struct ringbuf_t* rb_init(int bufsiz);

/* reclaim the ring buffer */
extern void rb_finalize(struct ringbuf_t* rb);

/* return the current number of elements in the buffer */
extern int rb_size(struct ringbuf_t* rb);

/* return non-zero (true) if the buffer is currently full */
extern int rb_is_full(struct ringbuf_t* rb);

/* return non-zero (true) if the buffer is currently empty */
extern int rb_is_empty(struct ringbuf_t* rb);

/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
extern void rb_insert(struct ringbuf_t* rb, int c);

/* retrieve a character at the front of the ring buffer; if the buffer is empty, the caller thread will be blocked */
extern int rb_remove(struct ringbuf_t* rb);

и это реализация буфера:

#include <malloc.h>
#include <stdio.h>

struct ringbuf_t 
{
    pthread_mutex_t mutex; /* lock to protect from simutaneous access to the buffer */
    pthread_cond_t cond_full; /* producer needs to wait when the buffer is full */
    pthread_cond_t cond_empty; /* consumer needs to wait when the buffer is empty */
    int bufsiz; /* size of the buffer; you may use an empty slot to differentiate the situation the buffer is full or empty */
    int front; /* index of the first element */
    int back; /* index next to the last element (or index to the first empty slot) */
    int count; //keeps track of the number of elements in the buffer
    char* buf; /* buffer itself */
};


struct ringbuf_t* rb_init(int bufsiz)
{
    struct ringbuf_t* buffer = (struct ringbuf_t*)malloc(sizeof(struct ringbuf_t));

    buffer->bufsiz = bufsiz;
    buffer->front = 0;
    buffer->back = 0;
    buffer->count = 0;
}


/* reclaim the ring buffer */
void rb_finalize(struct ringbuf_t* rb)
{
    free(rb);
    pthread_mutex_destroy(&rb->mutex);
    printf("\nnotice - ring buffer finalized");
}



/* return the current number of elements in the buffer */
int rb_size(struct ringbuf_t* rb)
{
    return (rb->count);
}



/* return non-zero (true) if the buffer is currently full */
int rb_is_full(struct ringbuf_t* rb)
{
    return ((rb->count) == rb->bufsiz);
}



/* return non-zero (true) if the buffer is currently empty */
int rb_is_empty(struct ringbuf_t* rb)
{
    return ((rb->count) == 0);
}



/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
void rb_insert(struct ringbuf_t* rb, int c)
{
    char* temp;


    if(rb->count < rb->bufsiz)
    {
        if(rb->count == 0)
        {
            rb->front = 0;
            rb->back = 0;
            rb->buf = c;
            rb->count++;
        }
        else
        {
            if(rb->front < (rb->bufsiz - 1))
            {
                temp = rb->buf;
                temp = temp + rb->front + 1;
                temp = c;
                rb->front++;
            }
            else if(rb->front == (rb->bufsiz -1))
            {
                rb->front = 0;
                rb->buf = c;
            }
        }
    }
    else
    {
        printf("\nerror - trying to insert into full buffer");
    }
}



/* retrieve a character at the tail (back) of the ring buffer; if the buffer is empty, the caller thread will be blocked */
int rb_remove(struct ringbuf_t* rb)
{
    if(rb->count != 0)
    {
        count--;
        if(rb->back < (rb->bufsiz-1)
        {
            rb->back++;
        }
        else if(rb->back == (rb->bufsiz -1))
        {
            rb->back = 0;
        }
    }
    else
    {
        printf("\nerror - trying to remove from empty buffer");
    }

}

это основной класс:

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


    //creating a static ring buffer
struct ringbuf* mybuffer = rb_init(10);

int thisChar;

    /*
     consumer thread, reads one character at a time, sets the lock when addinga character to the ring buffer
     while locked it checks if the buffer is full, waits for a slot, and then continues.
     */
void* consumer(void* arg)
{
    printf("consumer started");

    while(thisChar != EOF)
    {
        pthread_mutex_lock(&(mybuffer->mutex));

        while(rb_is_empty(mybuffer))
            pthread_cond_wait(&(mybuffer->cond_full), &(mybuffer->mutex));

        printf("%s", (char)rb_remove(mybuffer));

        pthread_cond_signal(&(mybuffer->cond_empty));

        pthread_mutex_unlock(&(mybuffer->mutex));
    }
}

    /*
     producer thread, takes one character at a time from the buffer, (while the buffer is not empty)
     and prints it to the screen.
     */


void* producer(void* arg)
{
    printf("producer started");

    while ((thisChar = getchar()) != EOF)
    {

        pthread_mutex_lock(&(mybuffer->mutex));

        while(rb_is_full(mybuffer))
            pthread_cond_wait(&(mybuffer->cond_empty), &(mybuffer->mutex));

        rb_insert(mybuffer, thisChar);

        pthread_cond_signal(&(mybuffer->cond_full));

        pthread_mutex_unlock(&(mybuffer->mutex));
    }
}


int main()
{

        //declaring threads
    pthread_t t0, t1;


        //creating threads as condumer, producer
    p_thread_create(&t0, NULL, consumer, (void*)mybuffer);
    p_thread_create(&t1, NULL, producer, (void*)mybuffer);


    pthread_join(t0, NULL);
    pthread_join(t1, NULL);

    rb_finalize(mybuffer);

    return 0;
}

Я скучаю по некоторым вещам, но мне нужно сначала пройти через это! пожалуйста, помогите!

Ответы [ 2 ]

6 голосов
/ 07 ноября 2011

Замените ваши #include <malloc.h> строки на #include <stdlib.h>. Это исправит ошибки, которые вы вставили сюда (и, вероятно, многие другие). Когда вы сделаете это, вернитесь к своему коду и удалите все приведения в ваших вызовах на malloc(3):

struct ringbuf_t* buffer = (struct ringbuf_t*)malloc(sizeof(struct ringbuf_t));

То, что (struct ringbuf_t*) не было необходимо с приблизительно 1989 , когда прототипы функций были вставлены в язык.

3 голосов
/ 07 ноября 2011

Смотри также:

ringbuf.h

Ваш заголовок ringbuf.h должен быть автономным и идемпотентным. Следовательно, он должен включать <pthread.h>.

#ifndef RINGBUF_H_INCLUDED
#define RINGBUF_H_INCLUDED

#include <pthread.h>

struct ringbuf_t 
{
    pthread_mutex_t mutex; /* lock to protect from simutaneous access to the buffer */
    pthread_cond_t cond_full; /* producer needs to wait when the buffer is full */
    pthread_cond_t cond_empty; /* consumer needs to wait when the buffer is empty */
    int bufsiz; /* size of the buffer; you may use an empty slot to differentiate the situation the buffer is full or empty */
    int front; /* index of the first element */
    int back; /* index next to the last element (or index to the first empty slot) */
    int count; //keeps track of the number of elements in the buffer
    char* buf; /* buffer itself */
};

/* return the pointer to the newl created and initialized ring buffer of the given size */
extern struct ringbuf_t* rb_init(int bufsiz);

/* reclaim the ring buffer */
extern void rb_finalize(struct ringbuf_t* rb);

/* return the current number of elements in the buffer */
extern int rb_size(struct ringbuf_t* rb);

/* return non-zero (true) if the buffer is currently full */
extern int rb_is_full(struct ringbuf_t* rb);

/* return non-zero (true) if the buffer is currently empty */
extern int rb_is_empty(struct ringbuf_t* rb);

/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
extern void rb_insert(struct ringbuf_t* rb, int c);

/* retrieve a character at the front of the ring buffer; if the buffer is empty, the caller thread will be blocked */
extern int rb_remove(struct ringbuf_t* rb);

#endif /* RINGBUF_H_INCLUDED */

Если бы это был мой заголовок, у меня была бы дополнительная строка:

typedef struct ringbuf_t ringbuf_t;

и я бы отредактировал прототипы функций, чтобы потерять ключевое слово struct.

Преимущество этого в том, что любой может включить ringbuf.h, и он просто будет работать для них.

ringbuf.c

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

Вам не следует использовать <malloc.h>, если вы не используете его расширенные функции. <stdlib.h> объявляет malloc() и др. И должен использоваться, если вы не знаете, какие дополнительные функции доступны в <malloc.h> и не используете их на самом деле.

Это приводит к:

#include "ringbuf.h"
#include <stdio.h>
#include <stdlib.h>

struct ringbuf_t* rb_init(int bufsiz)
{
    struct ringbuf_t* buffer = (struct ringbuf_t*)malloc(sizeof(struct ringbuf_t));

    buffer->bufsiz = bufsiz;
    buffer->front = 0;
    buffer->back = 0;
    buffer->count = 0;
}

/* reclaim the ring buffer */
void rb_finalize(struct ringbuf_t* rb)
{
    free(rb);
    pthread_mutex_destroy(&rb->mutex);
    printf("\nnotice - ring buffer finalized");
}

/* return the current number of elements in the buffer */
int rb_size(struct ringbuf_t* rb)
{
    return (rb->count);
}

/* return non-zero (true) if the buffer is currently full */
int rb_is_full(struct ringbuf_t* rb)
{
    return ((rb->count) == rb->bufsiz);
}

/* return non-zero (true) if the buffer is currently empty */
int rb_is_empty(struct ringbuf_t* rb)
{
    return ((rb->count) == 0);
}

/* insert (i.e., append) a character into the buffer; if the buffer is full, the caller thread will be blocked */
void rb_insert(struct ringbuf_t* rb, int c)
{
    char* temp;

    if(rb->count < rb->bufsiz)
    {
        if(rb->count == 0)
        {
            rb->front = 0;
            rb->back = 0;
            rb->buf = c;
            rb->count++;
        }
        else
        {
            if(rb->front < (rb->bufsiz - 1))
            {
                temp = rb->buf;
                temp = temp + rb->front + 1;
                temp = c;
                rb->front++;
            }
            else if(rb->front == (rb->bufsiz -1))
            {
                rb->front = 0;
                rb->buf = c;
            }
        }
    }
    else
    {
        printf("\nerror - trying to insert into full buffer");
    }
}

/* retrieve a character at the tail (back) of the ring buffer; if the buffer is empty, the caller thread will be blocked */
int rb_remove(struct ringbuf_t* rb)
{
    if(rb->count != 0)
    {
        count--;
        if(rb->back < (rb->bufsiz-1)
        {
            rb->back++;
        }
        else if(rb->back == (rb->bufsiz -1))
        {
            rb->back = 0;
        }
    }
    else
    {
        printf("\nerror - trying to remove from empty buffer");
    }
}

Вероятно, вам следует использовать fprintf(stderr, ...) вместо printf() для диагностики, и вам следует подумать, как отключить их во время выполнения (или, что более вероятно, как их включить).

Обратите внимание, что принято указывать системные заголовки в угловых скобках (отсюда <stdio.h>) и предоставленные пользователем заголовки в двойных кавычках (отсюда "ringbuf.h").

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

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

main.c

В C вы не можете инициализировать глобальную переменную с помощью вызова функции - в C ++ вы можете.

Следовательно, это не скомпилируется в C:

//creating a static ring buffer
struct ringbuf_t *mybuffer = rb_init(10);

Вы должны использовать:

struct ringbuf_t *mybuffer = 0;

, а затем в main() или функции, вызванной из main() - прямо или косвенно, - вызвать функцию:

mybuffer = rb_init(10);

Это будет до того, как вы начнете работу по созданию потоков. Когда ваш код rb_init() инициализирует переменные мьютекса и условия, ваш main() сможет продолжить работу, как написано.

До этого у вас есть много дел для очистки.


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

Примечание Если вы используете GCC, но не компилируете хотя бы с -Wall и предпочтительно -Wextra (и удалите все (все) предупреждения), вы упускаете важный помощник. Я работаю с ретроградными базами кода, где мне нужно беспокоиться и о -Wmissing-prototypes -Wold-style-definition -Wstrict-prototypes. Использование -Werror может быть полезным; это заставляет вас убрать предупреждения.

...