英文:
Golang's bytes.Buffer thread safety for one writer/one reader
问题
我知道golang的bytes.Buffer
不是线程安全的,但如果我有一个写入器(在一个goroutine中)和一个读取器(在另一个goroutine中),那么它是安全的吗?
如果不安全的话,为什么不安全?写入操作会将数据追加到缓冲区,而读取操作会从开头读取,所以我看不到它们会访问同一内存位置的情况。
英文:
I know that golang's bytes.Buffer
is not thread-safe but if I have one writer (in a goroutine) and one reader (in another goroutine). Is it safe?
If not, then why is it not? Write appends to the buffer while reads read from the start so I don't see a scenario where they will be accessing the same memory location.
答案1
得分: 9
不,这是不安全的。
bytes.Buffer
是一个结构体,Buffer.Read()
和 Buffer.Write()
方法都会读取/修改同一个结构体值的相同字段(它们都有指针接收器)。仅仅这一点就足以使其在并发使用时不安全。更多详情请参考 https://stackoverflow.com/questions/41406501/is-it-safe-to-read-a-function-pointer-concurrently-without-a-lock/41407827#41407827
此外,需要考虑到 bytes.Buffer
在字节切片中存储字节,而字节切片是结构体的一个字段。在写入时,有时可能需要分配一个更大的缓冲区(如果切片容量不足),因此必须更改切片头部(切片结构体字段)(在 Write()
中)。没有同步的情况下,无法保证并发的 Read()
将会看到这个更改。
而且...即使不需要重新分配(因为底层字节切片具有足够的容量来容纳传递给 Write()
的数据),将数据存储在字节切片中也需要对其进行重新切片,因此即使不需要重新分配,切片头部也会发生变化(切片的长度也是切片头部的一部分)。要查看切片头部的内容,请查看 reflect.SliceHeader
类型。
英文:
No, it's not safe.
bytes.Buffer
is a struct, and both the Buffer.Read()
and Buffer.Write()
methods read / modify the same fields of the same struct value (they have pointer receivers). This alone is enough to be unsafe for concurrent use. For more details, see https://stackoverflow.com/questions/41406501/is-it-safe-to-read-a-function-pointer-concurrently-without-a-lock/41407827#41407827
Also think about that a bytes.Buffer
stores bytes in a byte slice, which is a field of the struct. When writing, it might be necessary to sometimes allocate a bigger buffer (if slice capacity is not enough), and so the slice header (the slice struct field) must be changed (in Write()
). Without synchronization there's no guarantee that a concurrent Read()
will see this.
And... even if no reallocation is needed (because the underlying byte slice has enough capacity to accommodate the data passed to Write()
), storing the data in the byte slice requires to reslice it, so the slice header changes even if no reallocation is needed (the length of the slice is also part of the slice header). To see what's in the slice header, check out the reflect.SliceHeader
type.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论