环形队列
基础环形队列需要保证任意时刻至多只有一个生产者和一个消费者。多线程多生产者带来的问题:
P1、P2 同时向队列写入数据,同时读到 tail 为 0,然后 P1 执行写入,P2 再执行写入,最终两次写入都成功,但是 P1 的内容被 P2 覆盖。
解决:
#数据结构 留空法:
在循环队列中,求队列的长度也不仅仅是rear与front相减这么简单,因为,rear的值有可能比front小,这样相减结果是负值,显然不对。在求循环队列的长度时,都是用rear加上队列容量,减去front的值后,再对容量取模:(rear + MAXSIZE - front) % MAXSIZE
。留空法队满时头尾指针不相等,其他队满时头尾指针相等的方法,无法使用上述公式计算容量。
优点
- 简单、粗暴;
缺点
- 语义上实际可存数据量与缓冲区容量不一致
- 测试缓冲区是否满需要做取余数计算 ring buffer,一篇文章讲透它?
缓冲区(固定地址段)长度设为2的幂,是为了省略镜像指示位(用于判断缓冲区是否已满):如果读写指针的值相等,则缓冲区为空;如果读写指针相差n(缓冲区大小),则缓冲区为满;
我的疑问是:为什么缓冲区长度不是 2 的幂,就需要镜像指示位?
linux
中环形缓冲区(ring buffer)的实现,其实是对2.2.3节介绍的镜像指示位策略的扩展,读指针和写指针区间范围不再局限在镜像区间[0,2n−1],而是整个unsigned int
大小的空间,对于32位机器,读指针和写指针的区间范围是,[0,232−1];
- 进行扩展后,还能维持如下的关系,是因为缓冲区大小
n
会被扩展为2的幂,那么232肯定是n
扩展后的整数倍,所以还是能够满足如下关系;
读索引读指针缓冲区长度读索引=读指针&(缓冲区长度−1)写索引写指针缓冲区长度写索引=写指针&(缓冲区长度−1)
异或运算的性质和结果与 <code>a</code> 和 <code>b</code> 之间的关系: #
-
按位比较:
- 每一位上的异或结果由
a
和b
对应位的值决定。对于每一位,若a
和b
相同,则该位的结果为 0;若不同,则该位的结果为 1。
- 每一位上的异或结果由
-
无进位加法:
- 异或可以看作是对两个数按位加法的“无进位”版本。换句话说,它相当于在每一位上执行加法运算,但忽略了进位。例如,
1 + 1 = 10
,在普通加法中进位为 1,而在异或中,进位被忽略,只保留最低位的 0。
- 异或可以看作是对两个数按位加法的“无进位”版本。换句话说,它相当于在每一位上执行加法运算,但忽略了进位。例如,
-
交换律和结合律:
- 异或满足交换律和结合律,即:
- 交换律:
a ^ b = b ^ a
- 结合律:
(a ^ b) ^ c = a ^ (b ^ c)
- 交换律:
- 异或满足交换律和结合律,即:
-
自反性:
a ^ a = 0
:任何数与自身异或的结果是 0。这是因为每个位上相同的值异或会得到 0。
-
与 0 异或:
a ^ 0 = a
:任何数与 0 异或的结果是它本身。
最高有效位(英语:Most Significant Bit,msb),是指一个n位二进制数字中的n-1位,具有最高的权值2n−1
最低有效位(英语:Least Significant Bit,lsb)是指一个二进制数字中的第0位(即最低位),权值为2^0,可以用它来检测数的奇偶性。与之相反的称之为最高有效位。在大端序中,lsb指最右边的位。