使用循环缓冲区的多个生产者单个使

时间:2012-10-25 13:43:06

标签: c multithreading synchronization pthreads mutex

需要帮助才能让以下工作。

我有一个多个生产者线程(每个写入说100个字节的数据)到ringbuffer。 并且一个读取器(消费者)线程,一次读取100个字节并写入stdout。(最后我想根据数据写入文件)

通过这种实现,我有时会从环形缓冲区读取错误的数据。见下文 由于环形缓冲区大小很小,它会变满,一部分数据会丢失。这不是我目前的问题。

  

**问题:

  1. 在打印从ringbuffer读取的数据时,会得到一些数据 互换!!我无法找到这个bug。
  2. 逻辑/方法是否正确? (或)有没有 更好的方法
  3.   

    ringbuffer.h

    #define RING_BUFFER_SIZE  500
    struct ringbuffer
    {
        char *buffer;
        int wr_pointer;
        int rd_pointer;
        int size;
        int fill_count;
    };
    
      

    ringbuffer.c

    #include <stdio.h>
    #include <stdlib.h> 
    #include <string.h>
    #include "ringbuffer.h"
    
    int init_ringbuffer(char *rbuffer, struct ringbuffer *rb, size_t size)
    {
        rb->buffer = rbuffer;
        rb->size = size;
            rb->rd_pointer = 0;
            rb->wr_pointer = 0; 
            rb->fill_count = 0;
        return 0;
    }
    
    int rb_get_free_space (struct ringbuffer *rb)
    { 
        return (rb->size -  rb->fill_count);
    }
    
    int rb_write (struct ringbuffer *rb, unsigned char * buf, int len)
    {
        int availableSpace;
        int i;
    
        availableSpace = rb_get_free_space(rb);
        printf("In Write AVAIL SPC=%d\n",availableSpace);
        /* Check if Ring Buffer is FULL */
        if(len > availableSpace)
        {
           printf("NO SPACE TO WRITE - RETURN\n");
           return -1;
        }
    
        i = rb->wr_pointer;
        if(i == rb->size) //At the end of Buffer 
        {
           i = 0;
        }    
        else if (i + len > rb->size)
        {
            memcpy(rb->buffer + i, buf, rb->size - i);
            buf += rb->size - i;
            len = len - (rb->size - i);
            rb->fill_count += len;
            i = 0;
        }
        memcpy(rb->buffer + i, buf, len);
        rb->wr_pointer = i + len;
        rb->fill_count += len;
    
        printf("w...rb->write=%tx\n", rb->wr_pointer );
        printf("w...rb->read=%tx\n", rb->rd_pointer );
        printf("w...rb->fill_count=%d\n", rb->fill_count );
        return 0;
    }
    
    int rb_read (struct ringbuffer *rb, unsigned char * buf, int max)
    {
        int i;
    
        printf("In Read,Current DATA size in RB=%d\n",rb->fill_count);
        /* Check if Ring Buffer is EMPTY */
        if(max > rb->fill_count) 
        {
          printf("In Read, RB EMPTY - RETURN\n");
          return  -1; 
        }  
    
        i = rb->rd_pointer;
        if (i == rb->size)
        {
           i = 0;
        }
        else if(i + max > rb->size)
        {
            memcpy(buf, rb->buffer + i, rb->size - i);
            buf += rb->size - i;
            max = max - (rb->size - i);
            rb->fill_count -= max;
            i = 0;
        }
        memcpy(buf, rb->buffer + i, max);
        rb->rd_pointer = i + max;
        rb->fill_count -= max;
    
        printf("r...rb->write=%tx\n", rb->wr_pointer );
        printf("r...rb->read=%tx\n", rb->rd_pointer );
        printf("DATA READ ---> %s\n",(char *)buf);
        printf("r...rb->fill_count=%d\n", rb->fill_count );
        return 0;
    }
    

3 个答案:

答案 0 :(得分:0)

在制作人处,您还需要等待has empty space条件的条件变量。应该无条件地发信号通知两个条件变量,即当消费者从环形缓冲区中移除一个元素时,它应该向生产者发出信号;当生产者在缓冲区放置东西时,它应该向消费者发出信号。 此外,我会将此等待/信令逻辑移动到rb_read和rb_write实现中,因此您的环形缓冲区对于您的其余程序来说是一个“完全使用的解决方案”。

答案 1 :(得分:0)

关于你的问题 - 我也找不到那个错误 - 事实上,我已经尝试过你的代码并且没有看到这种行为。 2.你问这个逻辑/方法是否正确 - 好吧,就其而言,这确实实现了一种环形缓冲区。您的测试用例恰好具有大小的整数倍,并且记录大小是常量,因此这不是最佳测试。

在尝试你的代码时,我发现有很多线程饥饿 - 第一个运行的生成器线程(最后创建的)遇到的事情非常困难,在第5次尝试和失败后将东西放入缓冲区,不给消费者线程一个运行(甚至开始)的机会。然后,当消费者线程启动时,它会在释放cpu之前保持一段时间,并且下一个生产者线程最终启动。这就是它在我的机器上的工作方式 - 在不同的机器上会有所不同,我敢肯定。

现在的代码无法结束 - 创建10或100 MB的文件太难了......难以理解。

答案 2 :(得分:0)

(对于作者来说可能稍后,但如果其他人搜索“多个生产者单个消费者”)

我认为该实现中的基本问题是rb_write修改了一个全局状态(rb-> fill_count和其他rb-> XX),而没有在多个编写器之间进行任何同步。

有关其他想法,请查看:http://www.linuxjournal.com/content/lock-free-multi-producer-multi-consumer-queue-ring-buffer