声明:该系列文章是基于对@golangspec、go官方文档、《Go语言标准库》的学习汇总而成
bufio封装了io.Reader和io.Writer并且通过缓存来提高性能。
bufio.Writer
为什么需要buffer?
在没有buffer的情况下,我们的writer函数会调用系统调用来完成一次写入,而小量写入下,系统调用的时间花销远远大于写入本身的花销。因此,过多的小量写入会大大影响性能。
因此我们可以使用bufio来封装writer,在内部,每次的write会变成写入buffer而不是直接写入磁盘。下面是一个四个字节的buffer的示意,我们可以看到,通过使用buffer,我们将原先需要的9次write调用变为了两次,从而大大提高了性能
producer buffer destination (io.Writer)
a -----> a
b -----> ab
c -----> abc
d -----> abcd
e -----> e ------> abcd
f -----> ef abcd
g -----> efg abcd
h -----> efgh abcd
i -----> i ------> abcdefgh
使用bufio.Writer
我们可以通过调用bufio.NewWriter(io.Writer)*Writer
来创建有buffer的writer。
在内部,会默认使用4096字节作为buffer,如果希望修改buffer大小可以使用bufio.NewWriterSize(io.Writer,int)*Writer
。
什么时候实质写入(调用底层writer的Write)
当buffer存满或者手动调用Flush()
函数的时候,bufio.Writer会调用底层Write写入buffer数据。
如果不自行调用Flush的话,bufio.Writer的buffer中可能会有数据未写入。
什么时候会触发错误?对错误如何处理?
当因为Flush或者buffer已满而调用Write的时候可能会触发错误。一旦某次Write调用触发了错误,再次写入会不执行而直接返回该错误。
当写入大于buffer容量的数据时,bufio.Writer如何处理?
当我们要写入的数据量(或者填满了当前buffer后的剩余数据量)大于buffer容量时候,会直接调用底层Write函数而不是存入buffer
如何获知当前已使用的buffer容量?
可以使用bufio.Writer.Buffered()
获取
如何重用buffer避免不必要的内存分配?
我们可以使用bufio.Writer.Reset(io.Writer)
来重新设置底层writer进而实现重用buffer的目的。
值得注意的是,该函数调用会直接清空buffer,因此在调用之前应当使用Flush处理buffer中的剩余数据。
如何获知有多少剩余空间?
可以使用bufio.Writer.Available()
bufio是否实现了ReaderFrom优化?有什么注意事项?
虽然bufio实现了ReaderFrom方法来优化io.Copy
等函数的性能,但是仍然要使用Flush来写入剩余数据!
type Writer int
func (*Writer) Write(p []byte) (n int, err error) {
fmt.Printf("%q\n", p)
return len(p), nil
}
func main() {
s := strings.NewReader("onetwothree")
w := new(Writer)
bw := bufio.NewWriterSize(w