# 数据通信过程的免锁设计
数据通信过程一般都会用到buffer,为了保持数据一致性,通常会加锁保护。
是否真的需要加锁保护,需要分析buffer的具体使用场景。
buffer数据使用可以分为两个角色:生产者(存入者)和消费者(读取者)。 生产者和消费者分别在数据的前后两端操作buffer。
作为生产者添加数据之前获取 可用空间,加入此时有消费者在读取数据,可用空间只会增大,不会影响数据添加。反之,消费者在读取数据之前获取可读取数据,如果同时又生产者向缓存中添加数据,并不会影响消费者读取。
所以对于一个具体buffer而言,这两个角色在系统中具有唯一性,就可以做到免锁设计。 如果存在多个,则需要加锁保护,避免添加的数据互相覆盖或者消费的数据重复读出。
对于uart通信来说,中断处理程序就是其接收buffer唯一的生存者,发送buffer唯一的消费者,这样,它就可以自由的在buffer的一端进行操作,而不会对数据产生影响。
这里一般会考虑到buffer空间在操作过程是否有溢出覆盖的问题。
对于这个问题有成熟的解决方案,就是环形buffer的设计。
环形buffer有一个head和end指针分别用于生产者和消费者操作buffer的输入和输入的其中一端,只要这个环形buffer设计的没问题,就不用担心数据一致性问题。
如果考虑到串口数据接收不及时造成的数据丢失,这是另外一个问题,可以有硬件流控解决。
一个锁如果角色过多,设计就会混乱。
在多核情况下,用自旋锁实现原子操作,在中断里面是可以用的。原子操作不会导致调用者挂起。其他情况则不能使用,尤其持有自旋锁的过程再去申请其他锁。阿里的代码就存在这种使用。自旋锁要求每个核持有时间必须尽可能短,否则多核运行都被自旋锁串行化了,多核的性能就体现不出来了。