具有阻塞读取和非阻塞写入的循环/环形缓冲区?

时间:2013-02-05 09:09:44

标签: c blocking nonblocking circular-buffer

我正在用户空间中搜索C中的ringbuffer实现,所以我可以在我的库中使用它。

因为我需要一个带

的环形缓冲区
  • 非阻止写入(=覆盖最旧的数据)
  • 如果为空则阻止读取

我搜索了一段时间,并记得我使用过wait_event_interruptible& wake_up_interruptible在内核模式下执行类似的操作。

但是在用户空间中使用了什么,所以我可以搜索与该方法结合的ringbuffer?我不想重新发明轮子 - 周围有许多环形缓冲解决方案。

提前致谢&亲切的问候!

编辑:

似乎pthread_cond_wait可能等同于wait_event_interruptible

2 个答案:

答案 0 :(得分:6)

使用某些代码添加另一个答案,在我的其他答案中,这不是1:1匹配的伪代码。将此标记为维基答案,以防有人想要添加评论或进行其他改进。 C phtread互斥+条件变量实现非常简单的ringbuffer:

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

#define RINGBUFFER_SIZE (5)
int ringbuffer[RINGBUFFER_SIZE];
unsigned reader_unread = 0;
unsigned writer_next = 0;
pthread_mutex_t ringbuffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ringbuffer_written_cond = PTHREAD_COND_INITIALIZER;

void process_code(int ch) {
    int counter;
    printf("Processing code %d", ch);
    for(counter=5; counter>0; --counter) {
        putchar('.');
        fflush(stdout);
        sleep(1);
    }
    printf("done.\n");

}

void *reader() {
    pthread_mutex_lock(&ringbuffer_mutex);
    for(;;) {
        if (reader_unread == 0) {
            pthread_cond_wait(&ringbuffer_written_cond, &ringbuffer_mutex);
        }
        if (reader_unread > 0) {

            int ch;
            int pos = writer_next - reader_unread;
            if (pos < 0) pos += RINGBUFFER_SIZE;
            ch = ringbuffer[pos];
            --reader_unread;

            if (ch == EOF) break;

            pthread_mutex_unlock(&ringbuffer_mutex);
            process_code(ch);
            pthread_mutex_lock(&ringbuffer_mutex);
        }
    }
    pthread_mutex_unlock(&ringbuffer_mutex);

    puts("READER THREAD GOT EOF");
    return NULL;
}

void *writer() {
    int ch;
    do {
        int overflow = 0;
        ch = getchar();

        pthread_mutex_lock(&ringbuffer_mutex);

        ringbuffer[writer_next] = ch;

        ++writer_next;
        if (writer_next == RINGBUFFER_SIZE) writer_next = 0;

        if (reader_unread < RINGBUFFER_SIZE) ++reader_unread;
        else overflow = 1;

        pthread_cond_signal(&ringbuffer_written_cond);
        pthread_mutex_unlock(&ringbuffer_mutex);

        if (overflow) puts("WARNING: OVERFLOW!");

    } while(ch != EOF);

    puts("WRITER THREAD GOT EOF");
    return NULL;
}

int main(void)
{
    pthread_t reader_thread, writer_thread;

    puts("Starting threads. Type text and press enter, or type ctrl-d at empty line to quit.");
    pthread_create(&reader_thread, NULL, reader, NULL);
    pthread_create(&writer_thread, NULL, writer, NULL);

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    return 0;
}

答案 1 :(得分:0)

使用pthreads,标准方法是使用a mutex并使用waiting in one thread的条件变量,直到woken up by another

伪代码,编写器将暂时阻塞,但永远不会有不确定的时间,并且通过丢弃未读数据来处理缓冲区溢出:

作家写道:

acquire new data to write
lock mutex
get current writing position in buffer
compare to current reading position and check for overflow
    in case of overflow, update reading position (oldest data lost)
write new data to buffer
update writing position
do wakeup on condition variable
unlock mutex

读者阅读:

lock mutex
loop:
    get current reading position in buffer
    compare to current writing position in buffer
    if there's new data, break loop
    wait (possibly with timeout) on condition variable
    goto loop:
copy data from buffer
update reading position
unlock mutex
process copied data

显然上面,编写器可能会暂时阻止互斥锁,但由于读者只会暂时保留互斥锁(假设缓冲区中的数据相当短),这可能不是问题。

有关理解上述代码的重要细节:条件变量和互斥锁作为一对工作。等待条件变量将解锁互斥锁,一旦唤醒,只有在它可以重新锁定互斥锁之后才会继续。因此,在作者解锁互斥锁之前,读者才会真正继续。

当条件变量等待返回时,再次检查缓冲区位置是很重要的,而不是盲目地相信只需要向其添加更多数据的写入器就完成了唤醒。