├── .gitignore ├── LICENSE ├── README.md └── ringbuffer.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, skoowu 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ringbuffer 2 | ========== 3 | 4 | ringbuffer是用来替代Go语言的channel,提高海量数据收发的性能。目前只支持一个写。 5 | 6 | 7 | #####使用方法 8 | 9 | 10 | ring := ringbuffer.NewRing(100, 1000) 11 | 12 | // 一个写端 13 | go func() { 14 | var wbuf *ringbuffer.Buffer 15 | 16 | for i := 0; i < 10000; i++ { 17 | wbuf = ring.Write(wbuf, i) 18 | } 19 | ring.Stop(wbuf) 20 | }() 21 | 22 | // 10个读端 23 | var wg sync.WaitGroup 24 | 25 | for i := 0; i < 10; i++ { 26 | wg.Add(1) 27 | 28 | go func() { 29 | defer wg.Done() 30 | 31 | var ( 32 | rbuf *ringbuffer.Buffer 33 | e interface{} 34 | ) 35 | 36 | for { 37 | if e, rbuf = ring.Read(rbuf); rbuf == nil { 38 | break 39 | } 40 | log.Println(e.(int)) 41 | } 42 | }() 43 | } 44 | 45 | wg.Wait() 46 | 47 | -------------------------------------------------------------------------------- /ringbuffer.go: -------------------------------------------------------------------------------- 1 | package ringbuffer 2 | 3 | import ( 4 | "errors" 5 | "runtime" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | ) 10 | 11 | type Ring struct { 12 | buffers []*Buffer 13 | size int32 14 | rpos int32 15 | wpos int32 16 | stop int32 17 | firstW *Buffer 18 | } 19 | 20 | func NewRing(rsize, bsize int) *Ring { 21 | r := new(Ring) 22 | r.buffers = make([]*Buffer, rsize) 23 | r.size = int32(rsize) 24 | r.rpos = 0 25 | r.wpos = 0 26 | r.stop = 0 27 | 28 | for i := 0; i < rsize; i++ { 29 | buf := newBuffer(bsize) 30 | buf.index = int32(i) 31 | buf.ring = r 32 | 33 | r.buffers[i] = buf 34 | } 35 | 36 | r.firstW = r.prepareWrite() 37 | 38 | return r 39 | } 40 | 41 | func (r *Ring) prepareRead() *Buffer { 42 | var rpos, wpos int32 43 | 44 | for i := 0; i < 10; i++ { 45 | rpos = atomic.LoadInt32(&r.rpos) 46 | wpos = atomic.LoadInt32(&r.wpos) 47 | 48 | if rpos == wpos { 49 | if atomic.LoadInt32(&r.stop) == 1 { 50 | return nil 51 | } 52 | runtime.Gosched() 53 | continue 54 | } 55 | 56 | break 57 | } 58 | 59 | buffer := r.buffers[rpos] 60 | buffer.prepareGet() 61 | return buffer 62 | } 63 | 64 | func (r *Ring) Read(buf *Buffer) (e interface{}, next *Buffer) { 65 | if buf == nil { 66 | if buf = r.prepareRead(); buf == nil { 67 | return nil, nil 68 | } 69 | } 70 | 71 | for { 72 | if e = buf.get(); e == nil { 73 | if buf = r.prepareRead(); buf == nil { 74 | return 75 | } 76 | continue 77 | } 78 | next = buf 79 | return 80 | } 81 | } 82 | 83 | func (r *Ring) ExitRead(buf *Buffer) { 84 | buf.exitGet() 85 | } 86 | 87 | func (r *Ring) prepareWrite() *Buffer { 88 | buffer := r.buffers[r.wpos] 89 | buffer.preparePut() 90 | return buffer 91 | } 92 | 93 | func (r *Ring) Write(buf *Buffer, e interface{}) *Buffer { 94 | if buf == nil { 95 | buf = r.firstW 96 | } 97 | 98 | for { 99 | if err := buf.put(e); err != nil { 100 | buf = r.NextWrite(buf) 101 | continue 102 | } 103 | return buf 104 | } 105 | } 106 | 107 | func (r *Ring) NextWrite(buf *Buffer) (next *Buffer) { 108 | return buf.nextPut() 109 | } 110 | 111 | func (r *Ring) Stop(buf *Buffer) { 112 | atomic.AddInt32(&r.stop, 1) 113 | buf.finishPut() 114 | } 115 | 116 | type Buffer struct { 117 | body []interface{} 118 | w int32 119 | r int32 120 | lock sync.RWMutex 121 | once *sync.Once 122 | index int32 // Buffer在Ring数组中的下标 123 | ring *Ring 124 | readonly int32 125 | } 126 | 127 | func newBuffer(size int) *Buffer { 128 | buf := new(Buffer) 129 | buf.body = make([]interface{}, size) 130 | buf.once = new(sync.Once) 131 | return buf 132 | } 133 | 134 | func (b *Buffer) lastDo() { 135 | if b.index+1 == b.ring.size { 136 | atomic.StoreInt32(&b.ring.rpos, 0) 137 | } else { 138 | atomic.StoreInt32(&b.ring.rpos, b.index+1) 139 | } 140 | 141 | atomic.SwapInt32(&b.readonly, 0) 142 | } 143 | 144 | func (b *Buffer) prepareGet() { 145 | b.lock.RLock() 146 | } 147 | 148 | func (b *Buffer) finishGet() { 149 | b.once.Do(b.lastDo) 150 | b.lock.RUnlock() 151 | } 152 | 153 | func (b *Buffer) exitGet() { 154 | b.lock.RUnlock() 155 | } 156 | 157 | func (b *Buffer) get() (e interface{}) { 158 | defer func() { 159 | if e == nil { 160 | b.finishGet() 161 | } 162 | }() 163 | 164 | if atomic.LoadInt32(&b.r) >= b.w { 165 | return 166 | } 167 | newr := atomic.AddInt32(&b.r, 1) 168 | if newr <= b.w { 169 | e = b.body[newr-1] 170 | } 171 | 172 | return 173 | } 174 | 175 | func (b *Buffer) preparePut() { 176 | for { 177 | if atomic.LoadInt32(&b.readonly) == 0 { 178 | break 179 | } 180 | // TODO fix sleep 181 | time.Sleep(time.Millisecond * time.Duration(b.ring.size) / 2) 182 | } 183 | b.lock.Lock() 184 | b.r = 0 185 | b.w = 0 186 | b.once = new(sync.Once) 187 | } 188 | 189 | func (b *Buffer) finishPut() { 190 | atomic.AddInt32(&b.ring.wpos, 1) 191 | atomic.CompareAndSwapInt32(&b.ring.wpos, b.ring.size, 0) 192 | 193 | atomic.SwapInt32(&b.readonly, 1) 194 | b.lock.Unlock() 195 | } 196 | 197 | func (b *Buffer) nextPut() *Buffer { 198 | atomic.AddInt32(&b.ring.wpos, 1) 199 | atomic.CompareAndSwapInt32(&b.ring.wpos, b.ring.size, 0) 200 | 201 | next := b.ring.buffers[b.ring.wpos] 202 | next.preparePut() 203 | 204 | atomic.SwapInt32(&b.readonly, 1) 205 | b.lock.Unlock() 206 | 207 | return next 208 | } 209 | 210 | func (b *Buffer) put(e interface{}) error { 211 | if int(b.w) >= len(b.body) { 212 | return errors.New("full") 213 | } 214 | 215 | b.body[b.w] = e 216 | b.w++ 217 | 218 | return nil 219 | } 220 | --------------------------------------------------------------------------------