diff --git a/README.md b/README.md index 1bec424..0798e63 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,14 @@ Following is the simplest use case: ```go - // No need to initialize sync.WaitGroup, as it is initialized implicitly - p := mpb.New() // Star mpb container + var wg sync.WaitGroup + p := mpb.New() // Star mpb's rendering goroutine for i := 0; i < 3; i++ { - p.Wg.Add(1) // add wg counter + wg.Add(1) // add wg delta name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(100).PrependName(name, len(name)).AppendPercentage() go func() { + defer wg.Done() // you can p.AddBar() here, but ordering will be non deterministic for i := 0; i < 100; i++ { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) @@ -35,7 +36,8 @@ } }() } - p.WaitAndStop() // Wait for goroutines to finish + wg.Wait() // Wait for goroutines to finish + p.Stop() // Stop mpb's rendering goroutine // p.AddBar(1) // panic: you cannot reuse p, create new one! fmt.Println("finish") ``` diff --git a/example/io/multiple/main.go b/example/io/multiple/main.go index d534452..f866c06 100644 --- a/example/io/multiple/main.go +++ b/example/io/multiple/main.go @@ -7,6 +7,7 @@ "net/http" "os" "path/filepath" + "sync" "github.com/vbauerster/mpb" ) @@ -17,19 +18,22 @@ url1 := "https://homebrew.bintray.com/bottles/youtube-dl-2016.12.12.sierra.bottle.tar.gz" url2 := "https://homebrew.bintray.com/bottles/libtiff-4.0.7.sierra.bottle.tar.gz" + var wg sync.WaitGroup p := mpb.New().SetWidth(60) for i, url := range [...]string{url1, url2} { - p.Wg.Add(1) // if you omit this line, main will return without waiting for download goroutines + wg.Add(1) name := fmt.Sprintf("url%d:", i+1) - go download(p, name, url) + go download(&wg, p, name, url) } - p.WaitAndStop() + wg.Wait() + p.Stop() fmt.Println("Finished") } -func download(p *mpb.Progress, name, url string) { +func download(wg *sync.WaitGroup, p *mpb.Progress, name, url string) { + defer wg.Done() resp, err := http.Get(url) if err != nil { log.Printf("%s: %v", name, err) diff --git a/example/io/single/main.go b/example/io/single/main.go index 5e1db17..ca69334 100644 --- a/example/io/single/main.go +++ b/example/io/single/main.go @@ -36,9 +36,6 @@ defer dest.Close() p := mpb.New().SetWidth(64) - // if you omit the following line, download will complete fine, but rendering bar - // may not complete, thus better always use even in single thread. - p.Wg.Add(1) bar := p.AddBar(int(size)).PrependCounters(mpb.UnitBytes, 20).AppendETA() @@ -48,6 +45,6 @@ // and copy from reader, ignoring errors io.Copy(dest, reader) - p.WaitAndStop() + p.Stop() // if you omit this line, rendering bars goroutine will quit early fmt.Println("Finished") } diff --git a/example/simple/main.go b/example/simple/main.go index 72cb9c9..ca513fb 100644 --- a/example/simple/main.go +++ b/example/simple/main.go @@ -3,19 +3,21 @@ import ( "fmt" "math/rand" + "sync" "time" "github.com/vbauerster/mpb" ) func main() { - // No need to initialize sync.WaitGroup, as it is initialized implicitly - p := mpb.New() // Star mpb container + var wg sync.WaitGroup + p := mpb.New() // Star mpb's rendering goroutine for i := 0; i < 3; i++ { - p.Wg.Add(1) // add wg counter + wg.Add(1) // add wg delta name := fmt.Sprintf("Bar#%d:", i) bar := p.AddBar(100).PrependName(name, len(name)).AppendPercentage() go func() { + defer wg.Done() // you can p.AddBar() here, but ordering will be non deterministic for i := 0; i < 100; i++ { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) @@ -23,7 +25,8 @@ } }() } - p.WaitAndStop() // Wait for goroutines to finish + wg.Wait() // Wait for goroutines to finish + p.Stop() // Stop mpb's rendering goroutine // p.AddBar(1) // panic: you cannot reuse p, create new one! fmt.Println("finish") } diff --git a/progress.go b/progress.go index c54a316..755b762 100644 --- a/progress.go +++ b/progress.go @@ -31,12 +31,12 @@ // Progress represents the container that renders Progress bars type Progress struct { - Wg *sync.WaitGroup + // WaitGroup for internal rendering sync + wg *sync.WaitGroup out io.Writer width int sort SortType - // stopped bool op chan *operation rrChangeReqCh chan time.Duration @@ -60,7 +60,7 @@ outChangeReqCh: make(chan io.Writer), countReqCh: make(chan chan int), allDone: make(chan struct{}), - Wg: new(sync.WaitGroup), + wg: new(sync.WaitGroup), } go p.server(cwriter.New(os.Stdout), time.NewTicker(rr*time.Millisecond)) return p @@ -99,8 +99,12 @@ // AddBar creates a new progress bar and adds to the container func (p *Progress) AddBar(total int) *Bar { - bar := newBar(total, p.width, p.Wg) - p.op <- &operation{opBarAdd, bar, nil} + result := make(chan bool) + bar := newBar(total, p.width, p.wg) + p.op <- &operation{opBarAdd, bar, result} + if <-result { + p.wg.Add(1) + } return bar } @@ -118,11 +122,11 @@ return <-respCh } -// WaitAndStop stops listening -func (p *Progress) WaitAndStop() { +// Stop waits for bars to finish rendering and stops the rendering goroutine +func (p *Progress) Stop() { if !p.isAllDone() { close(p.allDone) - p.Wg.Wait() + p.wg.Wait() close(p.op) } } @@ -143,6 +147,7 @@ switch op.kind { case opBarAdd: bars = append(bars, op.bar) + op.result <- true case opBarRemove: var ok bool for i, b := range bars { @@ -165,7 +170,6 @@ sort.Sort(SortableBarSlice(bars)) } for _, b := range bars { - // cannot parallel this, because order matters fmt.Fprintln(cw, b) } cw.Flush()