分享

环形缓冲区(ring buffer),环形队列(ring queue) 原理

 goodwangLib 2017-09-29
1. 序言
环形缓冲区(ring buffer),环形队列(ring queue) 多用于2个线程之间传递数据,是标准的先入先出(FIFO)模型。一般来说,对于多线程共享数据,需要使用mutex来同步,这样共享数据才不至于发生不可预测的修改/读取,然而,mutex的使用也带来了额外的系统开销,ring buffer/queue 的引入,就是为了有效地解决这个问题,因其特殊的结构及算法,可以用于2个线程中共享数据的同步,而且必须遵循A线程push in,B线程pull out的原则。采用这个机制另外一个用处是A push完数据后可以马上接收另外一个数据,当输入数据快速时不容易造成数据阻塞而丢包.

线程 A                  媒介                    线程 B
data in --> ring buffer/queue --> data out

2. ring buffer 数据结构
  1. struct _RingB  
  2. {  
  3.     SI32 r_cursor;  
  4.     SI32 w_cursor;  
  5.     SI32 tr_cursor;  
  6.     SI32 tw_cursor;  
  7.     SI32 length;  
  8.     char data[0];  
  9. };  
ring buffer主要用于存储一段连续的数据块,例如网络接收的数据。
r_cursor 读指针,只在线程B中才能被修改,对于线程A,它是readonly的
tr_cursor 辅助读指针,只在线程B中才能被引用,用于计算当前有多少可读数据
w_cursor 写指针,只在线程A中才能被修改,对于线程B,它是readonly的
tw_cursor 辅助写指针,只在线程A中才能被引用,用于计算当前有多少空闲位置可以写入数据
length 缓冲区长度
data 缓冲区实体
需要注意的是,在data中,需要保留1byte的位置,因为:

a...

  push数据时,理论上来说,我们应该是从 thiz->tw_cursor = (thiz->w_cursor + 1) % thiz->length; 

开始写的,如果此时 thiz->tw_cursor == thiz->r_cursor, 我们认为此时缓冲区已满

b...

  pull数据时,也是相同的道理,thiz->r_cursor == thiz->w_cursor,我们认为此时缓冲区无数据可读

也就是说,r_cursor == w_cursor 是一个特殊的位置,所以我们需要在data里面保留1byte。
具体原理可参考 IDirectSoundBuffer8 

函数列表:
  1. RingB*  ringb_create(UI32 size);  
  2. void    ringb_destroy(RingB* thiz);  
  3. SI32    ringb_write_lock(RingB* thiz, SI8** ptr1, SI32* size1, SI8** ptr2, SI32* size2);  
  4. SI32    ringb_write_unlock(RingB* thiz, const SI8* ptr1, SI32 size1, const SI8* ptr2, SI32 size2);  
  5. SI32    ringb_write(SI8* ptr1, SI32* size1, SI8* ptr2, SI32* size2, const SI8* src, SI32 len);  
  6. SI32    ringb_read_lock(RingB* thiz, SI8** ptr1, SI32* size1, SI8** ptr2, SI32* size2);  
  7. SI32    ringb_read_unlock(RingB* thiz, const SI8* ptr1, SI32 size1, const SI8* ptr2, SI32 size2);  
  8. SI32    ringb_read(const SI8* ptr1, SI32* size1,const SI8* ptr2, SI32* size2, SI8* dst, SI32 len);  

2. ring queue 数据结构
  1. struct _RingQ  
  2. {  
  3.     SI32 r_cursor;  
  4.     SI32 w_cursor;  
  5.     SI32 length;  
  6.     void* data[0];  
  7. };  
ring buffer主要用于存储指针,这个指针指向共享数据的entry.
r_cursor 读指针,只在线程B中才能被修改,对于线程A,它是readonly的
w_cursor 写指针,只在线程A中才能被修改,对于线程B,它是readonly的
length 缓冲区长度
data 缓冲区实体
与ring buffer,在data中,同样需要保留1byte的位置,原理与ring buffer相似,只是tw_cursor,tw_cursor
用临时变量代替而已。

函数列表:
  1. RingQ*  ringq_create(SI32 size);  
  2. void    ringq_destroy(RingQ* thiz);  
  3. SI32    ringq_push(RingQ* thiz, void* data);  
  4. SI32    ringq_pop(RingQ* thiz, void** data);  
  5. SI32    ringq_fakePop(RingQ* thiz, void** data);  
  6. SI32    ringq_tryPush(RingQ* thiz);  
  7. SI32    ringq_tryPop(RingQ* thiz); 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多