美文网首页
Golang中Buffer高效拼接字符串以及自定义线程安全Buf

Golang中Buffer高效拼接字符串以及自定义线程安全Buf

作者: TomorrowWu | 来源:发表于2017-07-11 16:54 被阅读0次

    Go中可以使用“+”合并字符串,但是这种合并方式效率非常低,每合并一次,都是创建一个新的字符串,就必须遍历复制一次字符串。Java中提供StringBuilder类(最高效,线程不安全)来解决这个问题。Go中也有类似的机制,那就是Buffer(线程不安全)。

    以下是示例代码:
    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并发是不安全的

    如何自定义实现一个并发安全的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不能读也不能写,性能好

    相关文章

      网友评论

          本文标题:Golang中Buffer高效拼接字符串以及自定义线程安全Buf

          本文链接:https://www.haomeiwen.com/subject/pjymhxtx.html