在Go中压缩来自阅读器的数据

时间:2017-07-15 20:35:23

标签: go gzip

我在Go中编写了以下简短程序,试图透明地压缩阅读器中的数据(https://play.golang.org/p/SnvYT6it5r):

package main

import (
    "fmt"
    "io"
    "bytes"
    "compress/gzip"
)

func main() {
    data := bytes.NewReader([]byte("hello world"))
    compress(data)
}

func compress(data io.Reader) (io.Reader, error) {
    pr, pw := io.Pipe()
    gw := gzip.NewWriter(pw)

    n, err := io.Copy(gw, data)

    if err != nil {
        fmt.Printf("error: %s", err.Error())
    } else {
        fmt.Printf("%d bytes compressed", n)
    }
    return pr, err
}

当我运行它时,程序挂起:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_notifyListWait(0x1043e6cc, 0x0)
    /usr/local/go/src/runtime/sema.go:297 +0x140
sync.(*Cond).Wait(0x1043e6c4, 0x137118)
    /usr/local/go/src/sync/cond.go:57 +0xc0
io.(*pipe).write(0x1043e680, 0x1045a055, 0xa, 0xa, 0x0, 0x0, 0x0, 0x101)
    /usr/local/go/src/io/pipe.go:90 +0x1a0
io.(*PipeWriter).Write(0x1040c180, 0x1045a055, 0xa, 0xa, 0xe205ef63, 0x34c, 0x0, 0x0)
    /usr/local/go/src/io/pipe.go:157 +0x40
compress/gzip.(*Writer).Write(0x1045a000, 0x1040a130, 0xb, 0x10, 0x2c380, 0x7654, 0x1059e0, 0x111480)
    /usr/local/go/src/compress/gzip/gzip.go:168 +0x2e0
bytes.(*Reader).WriteTo(0x10440240, 0x190610, 0x1045a000, 0x0, 0xfef64000, 0x10440240, 0x1045a001, 0x190670)
    /usr/local/go/src/bytes/reader.go:134 +0xe0
io.copyBuffer(0x190610, 0x1045a000, 0x1905d0, 0x10440240, 0x0, 0x0, 0x0, 0x106620, 0x1045a000, 0x0, ...)
    /usr/local/go/src/io/io.go:380 +0x360
io.Copy(0x190610, 0x1045a000, 0x1905d0, 0x10440240, 0x10440240, 0x0, 0x1a47c0, 0x0)
    /usr/local/go/src/io/io.go:360 +0x60
main.compress(0x1905d0, 0x10440240, 0x10440240, 0x1040c170, 0x1040a130, 0xb)
    /tmp/sandbox403912545/main.go:19 +0x180
main.main()
    /tmp/sandbox403912545/main.go:12 +0xe0

导致死锁的原因是什么,从阅读器压缩数据的最有效方法是什么?

1 个答案:

答案 0 :(得分:3)

你写信给io.Pipe,但你从来没有读过它(在并行的例程中),因此死锁。以下是文档的说法:

  

管道上的读取和写入是一对一匹配的,除非需要多次读取以消耗单个写入。也就是说,每个写入PipeWriter的块都会阻塞,直到满足一个或多个完全消耗写入数据的PipeReader 读取为止。数据直接从Write复制到相应的Read(或Reads);没有内部缓冲。

https://golang.org/pkg/io/#Pipe