這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go中可以使用“+”合并字串,但是這種合并方式效率非常低,每合并一次,都是建立一個新的字串,就必須遍曆複製一次字串。Java中提供StringBuilder類(最高效,線程不安全)來解決這個問題。Go中也有類似的機制,那就是Buffer(線程不安全)。
以下是範例程式碼:
package mainimport ( "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並發是不安全的。
如何自訂實現一個並發安全的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源碼的基礎上自行實現
兩種鎖的區別
| sync.Mutex(互斥鎖) |
sync.RWMutex(讀寫鎖) |
| 當一個goroutine訪問的時候,其他goroutine都不能訪問,保證了資源的同步,避免了競爭,不過也降低了效能 |
非寫狀態時:多個Goroutine可以同時讀,一個Goroutine寫的時候,其它Goroutine不能讀也不能寫,效能好 |