Golang中Buffer高效拼接字符串以及自定義線程安全Buffer

本文原創文章,轉載註明出處,博客地址 https://segmentfault.com/u/to... 第一時間看後續精彩文章。以爲好的話,順手分享到朋友圈吧,感謝支持。bootstrap

Go中可使用「+」合併字符串,可是這種合併方式效率很是低,每合併一次,都是建立一個新的字符串,就必須遍歷複製一次字符串。Java中提供StringBuilder類(最高效,線程不安全)來解決這個問題。Go中也有相似的機制,那就是Buffer(線程不安全)。segmentfault

如下是示例代碼:
package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer
    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }
    fmt.Println(buffer.String())
}

使用bytes.Buffer來組裝字符串,不須要複製,只須要將添加的字符串放在緩存末尾便可。緩存

Buffer爲何線程不安全?

The Go documentation follows a simple rule: If it is not explicitly stated that concurrent access to something is safe, it is not.安全

==Go文檔遵循一個簡單的規則:若是沒有明確聲明併發訪問某事物是安全的,則不是。==併發

如下是Golang中bytes.Buffer部分源碼
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
    buf       []byte   // contents are the bytes buf[off : len(buf)]
    off       int      // read at &buf[off], write at &buf[len(buf)]
    bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
    lastRead  readOp   // last read operation, so that Unread* can work correctly.
}

// Write appends the contents of p to the buffer, growing the buffer as
// needed. The return value n is the length of p; err is always nil. If the
// buffer becomes too large, Write will panic with ErrTooLarge.
func (b *Buffer) Write(p []byte) (n int, err error) {
    b.lastRead = opInvalid
    m := b.grow(len(p))
    return copy(b.buf[m:], p), nil
}

// Read reads the next len(p) bytes from the buffer or until the buffer
// is drained. The return value n is the number of bytes read. If the
// buffer has no data to return, err is io.EOF (unless len(p) is zero);
// otherwise it is nil.
func (b *Buffer) Read(p []byte) (n int, err error) {
    b.lastRead = opInvalid
    if b.off >= len(b.buf) {
        // Buffer is empty, reset to recover space.
        b.Truncate(0)
        if len(p) == 0 {
            return
        }
        return 0, io.EOF
    }
    n = copy(p, b.buf[b.off:])
    b.off += n
    if n > 0 {
        b.lastRead = opRead
    }
    return
}

源碼對於Buffer的定義中,並無關於鎖的字段,在write和read函數中也未發現鎖的蹤跡,因此符合上面提到的文檔中的rule,即Buffer併發是不安全的app

如何自定義實現一個併發安全的Buffer

type Buffer struct {
    b bytes.Buffer
    rw sync.RWMutex
}
func (b *Buffer) Read(p []byte) (n int, err error) {
    b.rw.RLock()
    defer b.rw.RUnlock()
    return b.b.Read(p)
}
func (b *Buffer) Write(p []byte) (n int, err error) {
    b.rw.Lock()
    defer b.rw.Unlock()
    return b.b.Write(p)
}

經過讀寫鎖,解決併發讀寫問題,以上提供了Read和Write函數,親,是否是Golang代碼簡潔明瞭?其它函數能夠在Golang關於Buffer源碼的基礎上自行實現less

兩種鎖的區別
sync.Mutex(互斥鎖) sync.RWMutex(讀寫鎖)
當一個goroutine訪問的時候,其餘goroutine都不能訪問,保證了資源的同步,避免了競爭,不過也下降了性能 非寫狀態時:多個Goroutine能夠同時讀,一個Goroutine寫的時候,其它Goroutine不能讀也不能寫,性能好
相關文章
相關標籤/搜索