根据作者的性质和您使用它的方式,将所有内容保存在内存中(以便能够为以后加入的读者重新播放所有内容)是非常危险的,并且可能需要大量内存,或者导致您的应用由于内存不足而崩溃。
将它用于“低流量”记录器将所有内容保存在内存中可能没问题,但例如流式传输一些音频或视频很可能不行。
如果下面的读取器实现读取了写入缓冲区的所有数据,它们的Read() 方法将正确地报告io.EOF。必须小心,因为一旦遇到io.EOF,某些构造(例如bufio.Scanner)可能无法读取更多数据(但这不是我们实现的缺陷)。
如果您希望我们缓冲区的阅读器在缓冲区中没有更多可用数据时等待,等待新数据写入而不是返回io.EOF,您可以将返回的阅读器包装在提供的“尾部阅读器”中这里:Go: "tail -f"-like generator。
“内存安全”文件实现
这是一个非常简单和优雅的解决方案。它使用文件来写入,也使用文件来读取。同步基本上由操作系统提供。这不会冒内存错误的风险,因为数据仅存储在磁盘上。根据作者的性质,这可能足够,也可能不够。
我宁愿使用下面的接口,因为Close()在文件的情况下很重要。
type MyBuf interface {
io.WriteCloser
NewReader() (io.ReadCloser, error)
}
而且实现极其简单:
type mybuf struct {
*os.File
}
func (mb *mybuf) NewReader() (io.ReadCloser, error) {
f, err := os.Open(mb.Name())
if err != nil {
return nil, err
}
return f, nil
}
func NewMyBuf(name string) (MyBuf, error) {
f, err := os.Create(name)
if err != nil {
return nil, err
}
return &mybuf{File: f}, nil
}
我们的mybuf 类型嵌入了*os.File,因此我们可以“免费”获得Write() 和Close() 方法。
NewReader() 只是打开现有的支持文件进行读取(在只读模式下)并返回它,再次利用它实现了io.ReadCloser。
创建一个新的MyBuf 值是在NewMyBuf() 函数中实现的,如果创建文件失败,该函数也可能返回一个error。
注意事项:
请注意,由于mybuf 嵌入了*os.File,因此type assertion 可以“访问”os.File 的其他导出方法,即使它们不是MyBuf 接口的一部分。我不认为这是一个缺陷,但如果你想禁止这个,你必须将mybuf 的实现更改为不嵌入os.File,而是将它作为一个命名字段(但你必须添加@987654349 @ 和 Close() 方法自己,正确转发到 os.File 字段)。
内存实现
如果文件实现不充分,这里有一个内存实现。
由于我们现在只在内存中,我们将使用以下接口:
type MyBuf interface {
io.Writer
NewReader() io.Reader
}
这个想法是存储曾经传递给我们缓冲区的所有字节切片。当调用Read() 时,读取器将提供存储的切片,每个读取器将跟踪其Read() 方法提供了多少存储的切片。同步一定要处理,我们就用一个简单的sync.RWMutex。
废话不多说,实现如下:
type mybuf struct {
data [][]byte
sync.RWMutex
}
func (mb *mybuf) Write(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
// Cannot retain p, so we must copy it:
p2 := make([]byte, len(p))
copy(p2, p)
mb.Lock()
mb.data = append(mb.data, p2)
mb.Unlock()
return len(p), nil
}
type mybufReader struct {
mb *mybuf // buffer we read from
i int // next slice index
data []byte // current data slice to serve
}
func (mbr *mybufReader) Read(p []byte) (n int, err error) {
if len(p) == 0 {
return 0, nil
}
// Do we have data to send?
if len(mbr.data) == 0 {
mb := mbr.mb
mb.RLock()
if mbr.i < len(mb.data) {
mbr.data = mb.data[mbr.i]
mbr.i++
}
mb.RUnlock()
}
if len(mbr.data) == 0 {
return 0, io.EOF
}
n = copy(p, mbr.data)
mbr.data = mbr.data[n:]
return n, nil
}
func (mb *mybuf) NewReader() io.Reader {
return &mybufReader{mb: mb}
}
func NewMyBuf() MyBuf {
return &mybuf{}
}
请注意,Writer.Write() 的一般约定包括实现不得保留传递的切片,因此我们必须在“存储”它之前对其进行复制。
另请注意,Read() 的读者会尝试在最短的时间内锁定。也就是说,它只在我们需要来自缓冲区的新数据切片时才锁定,并且只进行读锁定,这意味着如果读取器有部分数据切片,它将在Read() 中发送,而不锁定和接触缓冲区。