package main import ( "github.com/cheggaaa/pb" "time" ) func main() { count := 100000 bar := pb.StartNew(count) for i := 0; i < count; i++ { bar.Increment() time.Sleep(time.Millisecond) } bar.FinishPrint("The End!") }
exampleに現実的なサンプルとしてファイルコピーの進捗状況をプログレスバーで表示するスクリプトがある。
https://github.com/cheggaaa/pb/blob/master/example/copy/copy.go
読んで気がついたことをメモ。
ProgressBarはio.Writerとio.Readerのinterfaceを実装している。
// implement io.Writer func (pb *ProgressBar) Write(p []byte) (n int, err error) { n = len(p) pb.Add(n) return } // implement io.Reader func (pb *ProgressBar) Read(p []byte) (n int, err error) { n = len(p) pb.Add(n) return }
AddメソッドはProgressbarのcurrentを増加させるのだけど、計算にatomicパッケージを使っている。
// Add to current value func (pb *ProgressBar) Add(add int) int { return int(atomic.AddInt64(&pb.current, int64(add))) }
io.MultiWriterに実際のファイルコピーのwriterとProgressBarのインスタンスを渡すことで、ファイルコピーの進捗状況をProgressBarで把握できるようにしている。
// create multi writer writer := io.MultiWriter(dest, bar) // and copy io.Copy(writer, source)
プログレスバーの描画はgoroutine上で行われている。
currentの読み出しもatomicパッケージを使っている。
// Start print func (pb *ProgressBar) Start() { pb.startTime = time.Now() if pb.Total == 0 { pb.ShowBar = false pb.ShowTimeLeft = false pb.ShowPercent = false } go pb.writer() }
func (pb *ProgressBar) writer() { var c, oc int64 oc = -1 for { if pb.isFinish { break } c = atomic.LoadInt64(&pb.current) if c != oc { pb.write(c) oc = c } time.Sleep(pb.RefreshRate) } }
currentへの書き込みと読み出しが並行で処理されるのでatomicパッケージを使う必要があるのかな