實現一個阻塞與非阻塞兩種方式的環形緩衝區RingBuffer

RingBuffer的實際使用場景很多.下面分別寫了阻塞和非阻塞兩種模式的代碼。

非阻塞模式可以自己替換ffmpeg裏面的fifo(同一線程先寫後讀),如果我們不想使用fifo,就可以直接使用下面的代碼。

如果是用SDL播放audio,存和讀在不同線程,那麼就需要阻塞方式。(下面實現的CGNonBlockRingBuffer寫是阻塞的,讀是非阻塞的,因爲很多場景下我們更加需要的是讀不要阻塞)

上述的兩種場景已通過下面代碼實際測試通過。

 

代碼在mac os上運行。windows平臺把sys/malloc.h改成malloc.h ,unistd.h改成Windows.h,代碼中usleep(1000)改成Sleep(1)

#include <stdio.h>
#include <iostream>
#include<sys/malloc.h>
#include <memory>
#include <pthread.h>
#include<unistd.h>
#include<string>
using namespace  std;


#define debug(fmt,...) printf("" __FILE__" %s:%d $ " fmt "\n", __FUNCTION__,  __LINE__,  ##__VA_ARGS__)


class CGBlockRingBuffer
{
    /**
    start:下一次數據讀的pos
    end: 下一次數據寫的pos
    end-start=available data 可讀取的數據區
    length-1-end:available space 可填充的數據區
    寫是阻塞模式,讀是非阻塞模式
    */

private:
    uint8_t *buffer;
    int length;
    int start;
    int end;
    pthread_mutex_t mutex;

    pthread_cond_t cond; //當隊列現在不滿了,生產者需要等待消費者發送此信號

    int abort_request;
public:

    CGBlockRingBuffer()
    {

    }
    ~CGBlockRingBuffer()
    {
        if (buffer) {
            free(buffer);
        }
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);

    }

    void Init(int size)
    {
        this->length = size + 1;
        start = 0;
        end = 0;
        abort_request = 0;
        buffer = (uint8_t*)malloc(this->length);
        memset(buffer, 0, this->length);

        pthread_mutex_init(&mutex, nullptr);
        pthread_cond_init(&cond, nullptr);

    }

    int Write(uint8_t *data, int size)
    {

        int ret = 0;
        if (size>this->length - 1){
            printf("size too large ! can not write\n");
            return  -1;
        }
        if (abort_request){
            return  -1;
        }

        pthread_mutex_lock(&mutex);
        if (end - start == 0) {
            start = end = 0;
        }


        while (size>this->length - end - 1){
            //printf("write no enough space: %d request, %d available\n", size, this->length - end - 1);

            pthread_cond_wait(&cond, &mutex);

            if (abort_request){
                ret = -1;
                break;
            }
        }
        if (!abort_request){

            if (size>0 && size <= this->length - end - 1){
                memcpy(buffer + end, data, size);

                end += size;

            }
        }

        pthread_mutex_unlock(&mutex);

        return ret;

    }

    int Read(uint8_t *target, int size)
    {

        int ret = 0;
        if (size>this->length - 1){
            printf("size too large ! can not read\n");
            return  -1;
        }
        if (abort_request){
            return  -1;
        }
        pthread_mutex_lock(&mutex);


        int available_data = end - start;
        int wsize = size > available_data ? available_data : size;
        memcpy(target, buffer + start, wsize);

        start += wsize;
        ret = wsize;

        if (end - start == 0) {
            start = end = 0;
        }

        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);

        return ret;
    }



    void  req_abort()
    {
        pthread_mutex_lock(&mutex);

        abort_request = 1;

        start = end = 0;

        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mutex);
    }

    void  req_activate(){
        pthread_mutex_lock(&mutex);

        abort_request = 0;
        start = end = 0;
        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mutex);
    }

};

class CGNonBlockRingBuffer
{
    /**
  start:下一次數據讀的pos
  end: 下一次數據寫的pos
  end-start=available data 可讀取的數據區
  length-1-end:available space 可填充的數據區
  非阻塞模式,初始size最好設置大一點,比如max(read size,write size)的3倍
*/

private:

    int length;
    int start;
    int end;
    uint8_t *buffer;
    int full;
    int empty;
public:

    CGNonBlockRingBuffer()
    {

    }
    ~CGNonBlockRingBuffer()
    {
        if (buffer) {
            free(buffer);
        }

    }

    void Init(int size)
    {
        this->length = size + 1;
        start = 0;
        end = 0;
        full=0;
        empty=1;

        buffer = (uint8_t*)malloc(this->length);
        memset(buffer,0,this->length);


    }

    int Write(uint8_t *data, int size)
    {

        int ret=0;
        if(size>this->length-1){
            printf("size too large ! can not write\n");
            return  -1;
        }

        if(full){
            printf("is full\n");
            return  -1;
        }else if(end>=start){
            int available_space=  this->length -  end - 1;

            if(size>available_space){
                int writesize=available_space;
                memcpy(buffer+end, data, writesize);

                if(size-writesize>start){
                    printf("error called,this length too small left%d  start%d\n",size-writesize,start);
                    return -1;
                }else{
                    memcpy(buffer, data+writesize, size-writesize);
                    end =(size-writesize);
                    if(end==start){
                        full=1;
                    }
                }


            }else{
                memcpy(buffer+end, data, size);
                end+=size;
            }

        }else  if(end<start){
            int available_space=  start -  end;
            if(size>available_space){
                printf("error2 called,this length too small \n");
                return -1;
            }else{
                memcpy(buffer+end, data, size);
                end+=size;
                if(end==start){
                    full=1;
                }
            }
        }
        if(ret==0){empty=0;}


        return ret;

    }

    int Read(uint8_t *target, int size)
    {

        int ret=0;
        if(size>this->length-1){
            printf("size too large ! can not read\n");
            return  -1;
        }

        if(empty){
            printf("is empty\n");
            return -1;
        }else if(end>=start){
            int available_data=end-start;
            if(size>available_data){
                printf("1read no enough data : has %d, needs %d\n",available_data,size);
                return -1;
            }else{
                memcpy(target,buffer+start,size);
                start+=size;

                if(start==end){
                    empty=1;
                    start=end=0;
                }

            }
        }else if(end<start){
            int available_data=length-1-start+end;
            if(size>available_data){
                printf("2read no enough data : has %d, needs %d\n",available_data,size);
                return -1;
            }else{
                if(size>=length-1-start){
                    memcpy(target,buffer+start,length-1-start);
                    memcpy(target+length-1-start,buffer,size-(length-1-start));
                    start=size-(length-1-start);

                }else{
                    memcpy(target,buffer+start,size);
                    start+=size;
                }

                if(start==end){
                    empty=1;
                    start=end=0;
                }
            }

        }


        if(ret==0){full=0;}

        return ret;
    }




};


void* func1(void* arg){
    CGBlockRingBuffer* blockRingBuffer=(CGBlockRingBuffer*)arg;
    char* a="abcdefghijklmn";
    for(int i=0;i<10;i++){
        if(i==6){
            blockRingBuffer->req_abort();
        }
        int ret=blockRingBuffer->Write((uint8_t*)a,10);
        if(ret<0)break;
        sleep(1);
    }

    printf("func1 end called\n");
    return NULL;

}

void* func2(void* arg){
    CGBlockRingBuffer* blockRingBuffer=(CGBlockRingBuffer*)arg;
    uint8_t* dst=(uint8_t*)malloc(16);
    for(int i=0;i<10;i++){
        memset(dst,0,16);
        int count=12;
        int rsize=0;
        while(count>0){

            int ret=blockRingBuffer->Read(dst+rsize,count);
             if(ret<0)break;
             count-=ret;
             rsize+=ret;

        }
        cout<<"dst:"<<dst<<endl;


        sleep(2);
    }
    printf("func2 end called\n");
    return NULL;

}

int main(int argc, char *argv[])
{   //以下是阻塞模式ringbuf測試
   CGBlockRingBuffer* blockRingBuffer=new CGBlockRingBuffer();
    blockRingBuffer->Init(16);

    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,func1,blockRingBuffer);
    pthread_create(&tid1,NULL,func2,blockRingBuffer);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    delete blockRingBuffer;

    //以下是非阻塞模式ringbuf測試

    CGNonBlockRingBuffer* ringBuffer=new CGNonBlockRingBuffer();
    ringBuffer->Init(30);

    char* a="abcdefghijklmn";
    uint8_t* dst=(uint8_t*)malloc(16);
    for(int i=0;i<10;i++){

        int ret=ringBuffer->Write((uint8_t*)a,10);
        if(ret<0)break;
        memset(dst,0,16);
        ret=ringBuffer->Read(dst,12);
        if(ret==0)
            cout<<"dst:"<<dst<<endl;

    }

    delete ringBuffer;
    free(dst);

    debug("main end called\n");
    return 0;
}












 

發佈了38 篇原創文章 · 獲贊 29 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章