Смотри также:
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
может быть полезным; это заставляет вас убрать предупреждения.