长时间运行的副本到bytes.Buffer

时间:2016-11-27 21:44:47

标签: go io buffer

我有一个很长的io.Reader每隔几秒钟返回一些数据(从不EOF),还有一个goroutine从该读者到io.Copy执行bytes.Buffer(也永远不会终止) )。像这样:

var src io.Reader
var buf bytes.Buffer

func main() {
    go io.Copy(&buf, src)
    // Do stuff. Read from the buffer periodically.
}

我不明白的是,当我尝试从缓冲区读取时,我看到了奇怪的结果。无论我是打电话给buf.Bytes()还是ioutil.ReadAll(&buf)还是其他什么都没关系,我只是看到一遍又一遍地写入缓冲区的第一个字节。

https://play.golang.org/p/yn0JPrvohV

我的问题是,我做错了什么?我可以这样使用bytes.Bufferio.Copy并定期阅读吗?

1 个答案:

答案 0 :(得分:1)

您无法将您的阅读调用与bytes.Bufferio.Copy上发生的写入同步。即使将bytes.Buffer包装在一个结构中以锁定读/写方法,当Read等待Read时,你将会发生死锁,而ReadAll在Read上被阻止。您需要手动执行复制,并正确序列化所有访问权限,或使用io.Pipe分隔读取和写入。

如果您使用FIFO(io.Pipe)来同步读取和写入,则不需要任何额外的锁定或通道来拖尾第一个io.Reader。这是一个示例read函数,可以在其缓冲区已满时打印,或者等待自上一个print语句以来的某个时间间隔:

func read(r io.Reader) {
    buf := make([]byte, 1024)
    pos := 0
    lastPrint := time.Now()
    for {
        n, err := r.Read(buf[pos:])
        if n > 0 {
            pos += n
        }

        if pos >= len(buf) || time.Since(lastPrint) > 125*time.Millisecond && pos > 0 {
            fmt.Println("read:", buf[:pos])
            lastPrint = time.Now()
            pos = 0
        }

        if err != nil {
            fmt.Println(err)
            break
        }
    }

    if pos > 0 {
        fmt.Println("read:", buf[:pos])
    }
}

func main() {
    pr, pw := io.Pipe()
    go func() {
        io.Copy(pw, &trickle{})
        pw.Close()
    }()
    read(pr)
}

https://play.golang.org/p/8NeV3v0LOU