并发无锁队列学习之二【单生产者单消费者】
2016-02-22 11:41
381 查看
转自:http://www.cnblogs.com/Anker/p/3864230.html
1、前言
最近工作比较忙,加班较多,每天晚上回到家10点多了。我不知道自己还能坚持多久,既然选择了就要做到最好。写博客的少了。总觉得少了点什么,需要继续学习。今天继续上个开篇写,介绍单生产者单消费者模型的队列。根据写入队列的内容是定长还是变长,分为单生产者单消费者定长队列和单生产者单消费者变长队列两种。单生产者单消费者模型的队列操作过程是不需要进行加锁的。生产者通过写索引控制入队操作,消费者通过读索引控制出队列操作。二者相互之间对索引是独享,不存在竞争关系。如下图所示:
![](http://images.cnitblog.com/i/305504/201407/232143026661228.png)
2、单生产者单消费者定长队列
这种队列要求每次入队和出队的内容是定长的,即生产者写入队列和消费者读取队列的内容大小事相同的。linux内核中的kfifo就是这种队列,提供了读和写两个索引。单生产者单消费者队列数据结构定义如下所示:
为了方便计算位置,设置队列的大小为2的次幂。这样可以将之前的取余操作转换为位操作,即r_index = r_index % size 与 r_index = r_index & (size -1)等价。位操作非常快,充分利用了二进制的特征。
(1)队列初始状态,读写索引相等,此时队列为空。
![](http://images.cnitblog.com/i/305504/201407/232216494799290.png)
(2)写入队列
写操作即进行入队操作,入队有三种场景,
2.1 写索引大于等于读索引
![](http://images.cnitblog.com/i/305504/201407/232225071351121.png)
![](http://images.cnitblog.com/i/305504/201407/232225596354081.png)
2.2写索引小于读索引
![](http://images.cnitblog.com/i/305504/201407/232228114324883.png)
2.3.写索引后不够写入一个
![](http://images.cnitblog.com/i/305504/201407/232228231042618.png)
(3)读取队列
读队列分为三种场景
3.1写索引大于等于读索引
![](http://images.cnitblog.com/i/305504/201407/232237263389057.png)
3.2写索引小于读索引
![](http://images.cnitblog.com/i/305504/201407/232237400417204.png)
3.3.读索引后面不够一个
![](http://images.cnitblog.com/i/305504/201407/232237510726067.png)
3、单生产者单消费者变长队列
有些时候生产者每次写入的数据长度是不确定的,导致写入队列的数据时变长的。这样为了充分利用队列,需要增加一个结束索引,保证队列末尾至少能够写入一个数据。变长队列数据结构定义如下:
冷静思考,勇敢面对,把握未来!
1、前言
最近工作比较忙,加班较多,每天晚上回到家10点多了。我不知道自己还能坚持多久,既然选择了就要做到最好。写博客的少了。总觉得少了点什么,需要继续学习。今天继续上个开篇写,介绍单生产者单消费者模型的队列。根据写入队列的内容是定长还是变长,分为单生产者单消费者定长队列和单生产者单消费者变长队列两种。单生产者单消费者模型的队列操作过程是不需要进行加锁的。生产者通过写索引控制入队操作,消费者通过读索引控制出队列操作。二者相互之间对索引是独享,不存在竞争关系。如下图所示:
![](http://images.cnitblog.com/i/305504/201407/232143026661228.png)
2、单生产者单消费者定长队列
这种队列要求每次入队和出队的内容是定长的,即生产者写入队列和消费者读取队列的内容大小事相同的。linux内核中的kfifo就是这种队列,提供了读和写两个索引。单生产者单消费者队列数据结构定义如下所示:
typedef struct { uint32_t r_index; /*读指针*/ uint32_t w_index; /*写指针*/ uint32_t size; /*缓冲区大小*/ char *buff[0]; /*缓冲区起始地址*/ }ring_buff_st;
为了方便计算位置,设置队列的大小为2的次幂。这样可以将之前的取余操作转换为位操作,即r_index = r_index % size 与 r_index = r_index & (size -1)等价。位操作非常快,充分利用了二进制的特征。
(1)队列初始状态,读写索引相等,此时队列为空。
![](http://images.cnitblog.com/i/305504/201407/232216494799290.png)
(2)写入队列
写操作即进行入队操作,入队有三种场景,
2.1 写索引大于等于读索引
![](http://images.cnitblog.com/i/305504/201407/232225071351121.png)
![](http://images.cnitblog.com/i/305504/201407/232225596354081.png)
2.2写索引小于读索引
![](http://images.cnitblog.com/i/305504/201407/232228114324883.png)
2.3.写索引后不够写入一个
![](http://images.cnitblog.com/i/305504/201407/232228231042618.png)
(3)读取队列
读队列分为三种场景
3.1写索引大于等于读索引
![](http://images.cnitblog.com/i/305504/201407/232237263389057.png)
3.2写索引小于读索引
![](http://images.cnitblog.com/i/305504/201407/232237400417204.png)
3.3.读索引后面不够一个
![](http://images.cnitblog.com/i/305504/201407/232237510726067.png)
3、单生产者单消费者变长队列
有些时候生产者每次写入的数据长度是不确定的,导致写入队列的数据时变长的。这样为了充分利用队列,需要增加一个结束索引,保证队列末尾至少能够写入一个数据。变长队列数据结构定义如下:
typedef struct { uint32_t r_index; /*读指针*/ uint32_t w_index; /*写指针*/ uint32_t e_index; /*队列结束指针*/ uint32_t size; /*缓冲区大小*/ char *buff[0]; /*缓冲区起始地址*/ }ring_buff_st;
冷静思考,勇敢面对,把握未来!
相关文章推荐
- Android am 指令的使用
- 搭建持续集成接口测试平台(Jenkins+Ant+Jmeter)
- IT男的别样人生,爱折腾,竟然辞职跑丽江去了
- IT男的别样人生,爱折腾,竟然辞职跑丽江去了
- 不只是打车软件,中国车主们赋予了Uber更多意义
- 站点被百度惩处应从哪些方面分析
- JVM调优总结(十)-调优方法
- 【转】libjpeg实现内存位图的压缩及解压缩-显示格式有问题
- 1031. 查验身份证(15)
- Android 开发框架介绍
- 并发无锁队列学习之一【开篇】
- [leetcode] 309. Best Time to Buy and Sell Stock with Cooldown
- Memcached与Redis缓存框架比较
- JVM调优总结(九)-新一代的垃圾回收算法
- gcc与vim的使用
- ActionBar和Fragment结合ViewPager实现TabActivity
- 获取Map集合中数据的方法
- 在js中获取当前页的记录数;页数;当前页。
- [BZOJ2654] tree
- 深入理解JSX