面试题答案
一键面试bufio.Reader结构体设计及关键方法实现逻辑
- 结构体设计
bufio.Reader
结构体用于包装一个io.Reader
,为其提供缓冲功能。其结构体定义大致如下(简化版,实际代码有更多字段):
type Reader struct { buf []byte rd io.Reader r, w int err error lastByte int lastRuneSize int }
buf
:用于存储读取数据的缓冲区。rd
:被包装的原始io.Reader
。r
:当前读位置。w
:当前写位置(用于缓冲区填充)。err
:读取过程中遇到的错误。lastByte
和lastRuneSize
:用于处理字符读取相关的辅助信息。
- 关键方法实现逻辑
- Read方法
- 首先检查缓冲区中是否有可用数据(
r < w
),如果有则直接从缓冲区读取数据到用户提供的p
切片中。 - 如果缓冲区中没有可用数据,调用
fill
方法填充缓冲区。fill
方法会从底层io.Reader
(rd
)读取数据到缓冲区(buf
),更新w
位置。 - 继续从缓冲区读取数据到
p
,直到p
被填满或者缓冲区无数据可读。
- 首先检查缓冲区中是否有可用数据(
- ReadByte方法
- 同样先检查缓冲区是否有数据,若有则返回缓冲区的下一个字节并更新读位置
r
。 - 若缓冲区无数据,调用
fill
方法填充缓冲区,然后再返回下一个字节。如果填充后仍无数据则返回错误。
- 同样先检查缓冲区是否有数据,若有则返回缓冲区的下一个字节并更新读位置
- ReadRune方法
- 先处理缓冲区中已有的数据,根据UTF - 8编码规则解析一个字符。
- 如果缓冲区数据不足,调用
fill
方法填充缓冲区,继续解析字符。 - 解析成功后返回字符、字符长度和错误信息。
- Read方法
bufio.Writer结构体设计及关键方法实现逻辑
- 结构体设计
bufio.Writer
结构体用于包装一个io.Writer
,提供缓冲写功能。其结构体定义大致如下(简化版):
type Writer struct { err error buf []byte n int wr io.Writer }
err
:写操作过程中遇到的错误。buf
:用于存储待写入数据的缓冲区。n
:当前缓冲区中已使用的字节数。wr
:被包装的原始io.Writer
。
- 关键方法实现逻辑
- Write方法
- 将用户提供的数据写入缓冲区
buf
。如果缓冲区空间不足,调用flush
方法将缓冲区数据写入底层io.Writer
(wr
),然后再尝试写入数据。 - 更新
n
表示缓冲区已使用的字节数。
- 将用户提供的数据写入缓冲区
- Flush方法
- 将缓冲区中所有数据(
buf[:n]
)写入底层io.Writer
(wr
)。 - 清空缓冲区(
n = 0
),并检查写入过程中是否发生错误。
- 将缓冲区中所有数据(
- Write方法
基于io.Reader和io.Writer接口设计更高效的缓冲读写器(适用于网络I/O场景)优化方向及核心代码示例
- 优化方向
- 缓冲区大小:根据网络I/O场景特点,合理调整缓冲区大小。对于网络传输,较大的缓冲区可能减少系统调用次数,但占用更多内存,需要权衡。一般可根据常见网络数据包大小设置为8192字节或更大。
- 减少内存分配:尽量复用已有的缓冲区,避免频繁创建新的缓冲区对象。
- 异步操作:利用Go语言的goroutine和channel实现异步读写,提高I/O并发性能。
- 零拷贝技术:在可能的情况下,使用零拷贝技术(如在Linux系统下利用
sendfile
系统调用)减少数据在用户空间和内核空间之间的拷贝次数,提高效率。
- 核心代码示例
package main
import (
"io"
"net"
"sync"
)
type AsyncBufferedReader struct {
conn net.Conn
buffer []byte
offset int
length int
wg sync.WaitGroup
dataCh chan []byte
doneCh chan struct{}
}
func NewAsyncBufferedReader(conn net.Conn, bufferSize int) *AsyncBufferedReader {
if bufferSize <= 0 {
bufferSize = 8192
}
ar := &AsyncBufferedReader{
conn: conn,
buffer: make([]byte, bufferSize),
dataCh: make(chan []byte),
doneCh: make(chan struct{}),
}
ar.wg.Add(1)
go ar.readLoop()
return ar
}
func (ar *AsyncBufferedReader) readLoop() {
defer func() {
close(ar.dataCh)
close(ar.doneCh)
ar.wg.Done()
}()
for {
n, err := ar.conn.Read(ar.buffer)
if err != nil && err != io.EOF {
// 处理错误
return
}
if n == 0 {
if err == io.EOF {
return
}
continue
}
ar.offset = 0
ar.length = n
ar.dataCh <- ar.buffer[:n]
}
}
func (ar *AsyncBufferedReader) Read(p []byte) (int, error) {
var data []byte
select {
case data = <-ar.dataCh:
case <-ar.doneCh:
return 0, io.EOF
}
if len(data) == 0 {
return 0, io.EOF
}
n := copy(p, data)
ar.offset += n
if ar.offset >= ar.length {
// 等待下一次数据
select {
case data = <-ar.dataCh:
case <-ar.doneCh:
return n, io.EOF
}
}
return n, nil
}
func (ar *AsyncBufferedReader) Close() error {
ar.conn.Close()
ar.wg.Wait()
return nil
}
type AsyncBufferedWriter struct {
conn net.Conn
buffer []byte
offset int
wg sync.WaitGroup
dataCh chan []byte
doneCh chan struct{}
}
func NewAsyncBufferedWriter(conn net.Conn, bufferSize int) *AsyncBufferedWriter {
if bufferSize <= 0 {
bufferSize = 8192
}
aw := &AsyncBufferedWriter{
conn: conn,
buffer: make([]byte, bufferSize),
dataCh: make(chan []byte),
doneCh: make(chan struct{}),
}
aw.wg.Add(1)
go aw.writeLoop()
return aw
}
func (aw *AsyncBufferedWriter) writeLoop() {
defer func() {
close(aw.doneCh)
aw.wg.Done()
}()
for data := range aw.dataCh {
_, err := aw.conn.Write(data)
if err != nil {
// 处理错误
return
}
}
}
func (aw *AsyncBufferedWriter) Write(p []byte) (int, error) {
select {
case aw.dataCh <- p:
case <-aw.doneCh:
return 0, io.ErrClosedPipe
}
return len(p), nil
}
func (aw *AsyncBufferedWriter) Close() error {
close(aw.dataCh)
aw.wg.Wait()
return aw.conn.Close()
}
在上述代码中,AsyncBufferedReader
和AsyncBufferedWriter
分别实现了异步的缓冲读写功能,通过goroutine
和channel
进行数据的异步处理,提高网络I/O的效率。