Go语言中的 bytes.Buffer 是否支持多线程安全?

22
在 Go 编程语言中,bytes.Buffer 是否支持多线程?据我所知,它的文档并未提及其线程安全性。
4个回答

31

不行。

Go语言的文档遵循一个简单的规则:如果没有明确说明对某个东西的并发访问是安全的,那么它就不是安全的。


9
只是好奇,这个规定有文件记录吗? - Ztyx
@Ztyx 有时候直接查看源代码可能更直观,我们很容易发现在bufio的Read/Write周围没有加锁操作。 - cifer

22

不行 - 但你可以轻松地将它包装在一个线程安全的结构体中!

对于简单的事情:

type Buffer struct {
    b bytes.Buffer
    m sync.Mutex
}
func (b *Buffer) Read(p []byte) (n int, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.Read(p)
}
func (b *Buffer) Write(p []byte) (n int, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.Write(p)
}
func (b *Buffer) String() string {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.String()
}

..并像往常一样使用var buf Buffer等。

需要更多的bytes.Buffer吗? 随意挑选:

func (b *Buffer) Bytes() []byte {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.Bytes()
}
func (b *Buffer) Cap() int {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.Cap()
}
func (b *Buffer) Grow(n int) {
    b.m.Lock()
    defer b.m.Unlock()
    b.b.Grow(n)
}
func (b *Buffer) Len() int {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.Len()
}
func (b *Buffer) Next(n int) []byte {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.Next(n)
}
func (b *Buffer) ReadByte() (c byte, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.ReadByte()
}
func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.ReadBytes(delim)
}
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.ReadFrom(r)
}
func (b *Buffer) ReadRune() (r rune, size int, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.ReadRune()
}
func (b *Buffer) ReadString(delim byte) (line string, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.ReadString(delim)
}
func (b *Buffer) Reset() {
    b.m.Lock()
    defer b.m.Unlock()
    b.b.Reset()
}
func (b *Buffer) Truncate(n int) {
    b.m.Lock()
    defer b.m.Unlock()
    b.b.Truncate(n)
}
func (b *Buffer) UnreadByte() error {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.UnreadByte()
}
func (b *Buffer) UnreadRune() error {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.UnreadRune()
}
func (b *Buffer) WriteByte(c byte) error {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.WriteByte(c)
}
func (b *Buffer) WriteRune(r rune) (n int, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.WriteRune(r)
}
func (b *Buffer) WriteString(s string) (n int, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.WriteString(s)
}
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
    b.m.Lock()
    defer b.m.Unlock()
    return b.b.WriteTo(w)
}

@xiaoyi Why rwmux? - Jack
@JackTuck 我的最新评论可能是 也许是 rwmux?。实际上,bytes.BufferRead() 中会更改数据,因此即使使用了 rwmux,仍必须使用写锁。但是,如果使用不同的缓冲区实现,则使用 rwmux 可能会增加吞吐量。 - xiaoyi
在我看来,使用互斥锁/锁本身就违背了CSP范式,不是吗?而Go语言正是建立在CSP之上的,不是吗? - latitov

7

使用io.Pipe()函数调用,它提供了一对连接对象(*PipeReader*PipeWriter),用于同步读/写。这可以并行完成,而且是线程安全的。


3
如果您能展示一些代码就太好了。对于像我这样的新手来说,“使用io.Pipe()”是一个很好的起点,但还有很多工作要做。 - Abhijit Sarkar

0

bytes.Buffer 不是线程安全的,但这里有一个使用“通道和通信”安全地写入 Buffer(或任何 io.StringWriter)的示例。

type SyncWriter struct {
    w      io.StringWriter
    finish sync.WaitGroup
    queue  chan string
    err    error
}

func NewSyncWriter(w io.StringWriter) *SyncWriter {
    var t SyncWriter
    t.w = w
    t.queue = make(chan string, 5)
    t.finish.Add(1)
    go t.writer()
    return &t
}

func (t *SyncWriter) Close() error {
    close(t.queue)
    t.finish.Wait()
    return t.err
}

func (t *SyncWriter) WriteString(s string) (int, error) {
    t.queue <- s
    return len(s), nil
}

func (t *SyncWriter) Write(p []byte) (int, error) {
    return t.WriteString(string(p))
}

func (t *SyncWriter) writer() {
    defer t.finish.Done()
    for s := range t.queue {
        _, err := t.w.WriteString(s)
        if err != nil && t.err == nil {
            t.err = err
        }
    }
}

以下是如何使用它:

func write(wg *sync.WaitGroup, w io.Writer, d int) {
    fmt.Fprintf(w, "%d", d)
    wg.Done()
}

func main() {
    var buf bytes.Buffer
    w := NewSyncWriter(&buf)
    w.WriteString("hello ")
    var wg sync.WaitGroup
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go write(&wg, w, i)
    }
    wg.Wait()
    w.Close()
    fmt.Printf("%s\n", buf.String())
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接