博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程安全的无锁RingBuffer的实现【一个读线程,一个写线程】
阅读量:2437 次
发布时间:2019-05-10

本文共 2384 字,大约阅读时间需要 7 分钟。

在程序设计中,我们有时会遇到这样的情况,一个线程将数据写到一个buffer中,另外一个线程从中读数据。所以这里就有多线程竞争的问题。通常的解决办法是对竞争资源加锁。但是,一般加锁的损耗较高。其实,对于这样的一个线程写,一个线程读的特殊情况,可以以一种简单的无锁RingBuffer来实现。这样代码的运行效率很高。

本文借鉴了项目代码。

代码我在github上放了一份,需要的同学可以去下载()。本文最后也会附上一份。

代码的基本原理如下。

如图所示,假定buffer的长度是bufferSize. 我们设置两个指针。head指向的是下一次读的位置,而tail指向的是下一次写的位置。由于这里是环形buffer (ring buffer),这里就有一个问题,怎样判断buffer是满或者空。这里采用的规则是,buffer的最后一个单元不存储数据。所以,如果head == tail,那么说明buffer为空。如果 head == tail + 1 (mod bufferSize),那么说明buffer满了。

接下来就是最重要的内容了:怎样以无锁的方式进行线程安全的buffer的读写操作。基本原理是这样的。在进行读操作的时候,我们只修改head的值,而在写操作的时候我们只修改tail的值。在写操作时,我们在写入内容到buffer之后才修改tail的值;而在进行读操作的时候,我们会读取tail的值并将其赋值给copyTail。赋值操作是原子操作。所以在读到copyTail之后,从headcopyTail之间一定是有数据可以读的,不会出现数据没有写入就进行读操作的情况。同样的,读操作完成之后,才会修改head的数值;而在写操作之前会读取head的值判断是否有空间可以用来写数据。所以,这时候tailhead - 1之间一定是有空间可以写数据的,而不会出现一个位置的数据还没有读出就被写操作覆盖的情况。这样就保证了RingBuffer的线程安全性。

最后附上代码供参考。欢迎批评指正,也欢迎各种讨论!

1 public class RingBuffer { 2  3     private final static int bufferSize = 1024; 4     private String[] buffer = new String[bufferSize]; 5     private int head = 0; 6     private int tail = 0; 7      8     private Boolean empty() { 9         return head == tail;10     }11     private Boolean full() {12         return (tail + 1) % bufferSize == head;13     }14     public Boolean put(String v) {15         if (full()) {16             return false;17         }18         buffer[tail] = v;19         tail = (tail + 1) % bufferSize;20         return true;21     }22     public String get() {23         if (empty()) {24             return null;25         }26         String result = buffer[head];27         head = (head + 1) % bufferSize;28         return result;29     }30     public String[] getAll() {31         if (empty()) {32             return new String[0];33         }34         int copyTail = tail;35         int cnt = head < copyTail ? copyTail - head : bufferSize - head + copyTail;36         String[] result = new String[cnt];37         if (head < copyTail) {38             for (int i = head; i < copyTail; i++) {39                 result[i - head] = buffer[i];40             }41         } else {42             for (int i = head; i < bufferSize; i++) {43                 result[i - head] = buffer[i];44             }45             for (int i = 0; i < copyTail; i++) {46                 result[bufferSize - head + i] = buffer[i];47             }48         }49         head = copyTail;50         return result;51     }52 }

转载地址:http://lugmb.baihongyu.com/

你可能感兴趣的文章
DELPHI下的多线程程序设计(2) (转)
查看>>
成功软件开发者的9种编程习惯 5 (转)
查看>>
VC常见入门问题总结(二) (转)
查看>>
成功软件开发者的9种编程习惯 4 (转)
查看>>
在Delphi中使用Queued 组件 (转)
查看>>
在Delphi中编写控件的基本方法 (转)
查看>>
通过XSL转换XML文件 (转)
查看>>
数据结构学习笔记(一) (转)
查看>>
用OTA下载本机j2me程序至手机 (转)
查看>>
在网页中使用realplayer播放rm文件 (转)
查看>>
用vb6制作随机产生文件把硬盘塞满! (转)
查看>>
限制并方便用户输入 (转)
查看>>
udp 500 D.O.S攻击 (转)
查看>>
向word文档中输出表格及图形 (转)
查看>>
没头没尾--项目开发笔记:工具能生成多少代码!? (转)
查看>>
Delphi使用VB编写的ActiveX控件全攻略 (转)
查看>>
开辟一条自由ASP快车道 (转)
查看>>
Java开发工具之我见(补) (转)
查看>>
也说说c++builder中的不规则窗体的实现 (转)
查看>>
.NET中的DataReader简介 (转)
查看>>