深入解析golang bufio

作者:charlieroro 时间:2024-04-28 09:13:24 

bufio 包介绍 

bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

golang bufio

当频繁地对少量数据读写时会占用IO,造成性能问题。golang的bufio库使用缓存来一次性进行大块数据的读写,以此降低IO系统调用,提升性能。

在Transport中可以设置一个名为WriteBufferSize的参数,该参数指定了底层(Transport.dialConn)写buffer的大小。

tr := &http.Transport{
WriteBufferSize:     64 * 1024,
}
pconn.br = bufio.NewReaderSize(pconn, t.readBufferSize())
pconn.bw = bufio.NewWriterSize(persistConnWriter{pconn}, t.writeBufferSize())

使用bufio进行写

可以使用bufio.NewWriter初始化一个大小为4096字节的Writer(见下),或使用bufio.NewWriterSize初始化一个指定大小的Writer

Writer中的主要参数为缓存区buf,缓存区中的数据偏移量n以及写入接口wr

type Writer struct {
err error
buf []byte
n   int
wr  io.Writer
}

bufio.Writer方法可以一次性写入缓存中的数据,通常有如下三种情况:

  • 缓存中满数据

  • 缓存中仍有空间

  • 待写入的数据大于缓存的大小

缓存中满数据

当缓存中满数据时,会执行写操作。

缓存中仍有空间

如果缓存中仍有数据,则不会执行写入动作,除非调用Flush()方法。

待写入的数据大于缓存的大小

由于此时缓存无法缓存足够的数据,此时会跳过缓存直接执行写操作

type Writer int
func (*Writer) Write(p []byte) (n int, err error) {
fmt.Printf("Writing: %s\n", p)
return len(p), nil
}
func main() {
w := new(Writer)
bw1 := bufio.NewWriterSize(w, 4)
// Case 1: Writing to buffer until full
bw1.Write([]byte{'1'})
bw1.Write([]byte{'2'})
bw1.Write([]byte{'3'})
bw1.Write([]byte{'4'}) // write - buffer is full
// Case 2: Buffer has space
   bw1.Write([]byte{'5'}) //此时buffer中无法容纳更多的数据,执行写操作,写入 []byte{'1','2','3','4'}
err = bw1.Flush() // forcefully write remaining
if err != nil {
panic(err)
}
// Case 3: (too) large write for buffer
// Will skip buffer and write directly
bw1.Write([]byte("12345")) //buffer不足,直接执行写操作
//结果:
Writing: 1234
Writing: 5
Writing: 12345

缓存重用

申请缓存对性能是有损耗的,可以使用Reset方法重置缓存,其内部只是将Writer的数据偏移量n置0。

wr := new(Writer)
bw := bufio.NewWriterSize(wr,2)
bw.Reset(wr)

获取缓存的可用空间数

Available()方法可以返回缓存的可用空间数,即len(Writer.buf)-Writer.n

使用bufio进行读

与用于写数据的Writer类似,读数据也有一个Reader,可以使用NewReader初始化一个大小为4096字节的Reader,或使用NewReaderSize初始化一个指定大小的Reader(要求最小为16字节)。Reader也有一个记录偏移量的变量r

type Reader struct {
buf          []byte
rd           io.Reader // reader provided by the client
r, w         int       // buf read and write positions
err          error
lastByte     int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

Peek

该方法会返回buf中的前n个字节的内容,但与Read操作不同的是,它不会消费缓存中的数据,即不会增加数据偏移量,因此通常也会用于判断是否读取结束(EOF)。通常有如下几种情况:

  • 如果peak的值小于缓存大小,则返回相应的内容

  • 如果peak的值大于缓存大小,则返回bufio.ErrBufferFull错误

  • 如果peak的值包含EOF且小于缓存大小,则返回EOF

Read

将数据读取到p,涉及将数据从缓存拷贝到p

func (b *Reader) Read(p []byte) (n int, err error)

ReadSlice

该方法会读从缓存读取数据,直到遇到第一个delim。如果缓存中没有delim,则返回EOF,如果查询的长度超过了缓存大小,则返回 io.ErrBufferFull 错误。

func (b *Reader) ReadSlice(delim byte) (line []byte, err error)

例如delim',',则下面会返回的内容为1234,

r := strings.NewReader("1234,567")
rb := bufio.NewReaderSize(r, 20)
fmt.Println(rb.ReadSlice(','))
// 结果:[49 50 51 52 44] <nil>

注意:ReadSlice返回的是原始缓存中的内容,如果针对缓存作并发操作,则返回的内容有可能被其他操作覆盖。因此在官方注释里面有写,建议使用ReadBytesReadString。但ReadBytesReadString涉及内存申请和拷贝,因此会影响性能。在追求高性能的场景下,建议外部使用sync.pool来提供缓存。

// Because the data returned from ReadSlice will be overwritten
// by the next I/O operation, most clients should use
// ReadBytes or ReadString instead.

ReadLine

ReadLine() (line []byte, isPrefix bool, err error)

ReadLine底层用到了ReadSlice,但在返回时会移除\n 或\r\n。需要注意的是,如果切片中没有找到换行符,则不会返回EOF或io.ErrBufferFull 错误,相反,它会将isPrefix置为true

ReadBytes

ReadSlice类似,但它会返回一个新的切片,因此便于并发使用。如果找不到delimReadBytes会返回io.EOF

func (b *Reader) ReadBytes(delim byte) ([]byte, error)

Scanner

scanner可以不断将数据读取到缓存(默认64*1024字节)。

rb := strings.NewReader("12345678901234567890")
scanner := bufio.NewScanner(rb)
for scanner.Scan() {
fmt.Printf("Token (Scanner): %q\n", scanner.Text())
}
// 结果:Token (Scanner): "12345678901234567890"

参考

how-to-read-and-write-with-golang-bufio

来源:https://www.cnblogs.com/charlieroro/p/16172519.html

标签:golang,bufio
0
投稿

猜你喜欢

  • php5.4以下版本json不支持不转义内容中文的解决方法

    2023-07-02 17:10:45
  • 教你制作1px边框表格的四种方法

    2008-10-04 10:16:00
  • MYsql库与表的管理及视图介绍

    2024-01-25 21:33:06
  • 第二篇Bootstrap起步

    2024-04-16 08:59:36
  • python代码检查工具pylint 让你的python更规范

    2021-07-03 12:06:38
  • python根据list重命名文件夹里的所有文件实例

    2022-02-12 15:54:30
  • Pandas对每个分组应用apply函数的实现

    2022-01-26 04:02:00
  • echarts学习之legend点击事件解读

    2024-05-03 11:10:17
  • Python处理键映射值操作详解

    2021-03-21 03:14:53
  • python实现批量解析邮件并下载附件

    2023-07-05 02:32:16
  • Python面向对象之私有属性和私有方法应用案例分析

    2021-08-19 07:08:29
  • C#数据库操作的示例详解

    2024-01-15 05:46:32
  • 教你如何使用php session

    2023-11-15 06:28:01
  • pycharm from lxml import etree标红问题及解决

    2021-12-10 05:55:38
  • 谈谈为什么你的 JavaScript 代码如此冗长

    2024-04-17 09:50:07
  • Python实现随机从图像中获取多个patch

    2021-09-19 13:39:56
  • 写一个Python脚本自动爬取Bilibili小视频

    2022-04-29 04:10:48
  • Python 安装第三方库 pip install 安装慢安装不上的解决办法

    2023-02-23 13:43:45
  • vue element table 表格请求后台排序的方法

    2024-05-29 22:49:37
  • 一文学会Mysql数据库备份与恢复

    2024-01-26 10:17:08
  • asp之家 网络编程 m.aspxhome.com