2016-11-27 6 views
1

私は、数秒ごとに(EOFではない)いくつかのデータを返すように、そしてio.Copybytes.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.Bufferを使用することができますか(io.Copyと定期的に読んでください)。

+3

bytes.Buffer上のデータのレースがあります。ここではどちらかのプリントそのバッファがいっぱいになっている、または最後のprint文ので、いくつかの間隔を待つ例read機能です。あなたのアプリケーションを実行してください。(競争検出機能付き)(https://golang.org/doc/articles/race_detector.html)。 –

+0

また、解決しようとしている上位レベルの問題についても説明してください。その問題に対する最良の解決法は、バイトを伴わないかもしれない。バッファ。 –

+0

データ競合条件の他に、コピー・ゴルーチンと読み取りゴルーチンとの間に論理的な同期がないため、読み取りが完了する前にプログラムが終了したり、コピー機能が読み取り呼び出しとリセット呼び出しの間にバッファーに書き込む可能性があります、データを失う。読み込みループと書き込みループの同期が必要です。または、io.PipeのようにFIFOの反対側にある必要があります。 (あなたもシングルケースselect文は必要ありません) – JimB

答えて

1

io.Copybytes.Bufferで発生している書き込みと読み取り呼び出しを同期させることはできません。 Read/Writeメソッドをロックする構造体にbytes.Bufferをラップしても、ReadAllがReadでブロックされている間にCopyがWriteを待機していると、デッドロックになります。手動でコピーを実行し、すべてのアクセスをシリアル化するか、またはio.Pipeで読み取りと書き込みを分離する必要があります。

FIFO(io.Pipe)を使用して読み込みと書き込みを同期させる場合は、先頭の末尾に余分なロックやチャネルは必要ありません。io.Reader

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

+0

ありがとうございます。これは、問題を解決するよりスマートな方法です。私は長い実行中のioを実行する必要があると思います。私は間違いなく、バイトではなくパイプを使用します。バッファ、特に同時アクセスでは安全でないため、最初は気付きませんでした。私はパイプの反対側にバッファリングすることができます。 – Dave

+1

@Dave:Goの_no_データ構造は、特に明記されていない限り、同時アクセスで安全です。 – JimB

関連する問題