don't expose internal wg
Vladimir Bauer
9 years ago
| 20 | 20 | Following is the simplest use case: |
| 21 | 21 | |
| 22 | 22 | ```go |
| 23 | // No need to initialize sync.WaitGroup, as it is initialized implicitly | |
| 24 | p := mpb.New() // Star mpb container | |
| 23 | var wg sync.WaitGroup | |
| 24 | p := mpb.New() // Star mpb's rendering goroutine | |
| 25 | 25 | for i := 0; i < 3; i++ { |
| 26 | p.Wg.Add(1) // add wg counter | |
| 26 | wg.Add(1) // add wg delta | |
| 27 | 27 | name := fmt.Sprintf("Bar#%d:", i) |
| 28 | 28 | bar := p.AddBar(100).PrependName(name, len(name)).AppendPercentage() |
| 29 | 29 | go func() { |
| 30 | defer wg.Done() | |
| 30 | 31 | // you can p.AddBar() here, but ordering will be non deterministic |
| 31 | 32 | for i := 0; i < 100; i++ { |
| 32 | 33 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) |
| 34 | 35 | } |
| 35 | 36 | }() |
| 36 | 37 | } |
| 37 | p.WaitAndStop() // Wait for goroutines to finish | |
| 38 | wg.Wait() // Wait for goroutines to finish | |
| 39 | p.Stop() // Stop mpb's rendering goroutine | |
| 38 | 40 | // p.AddBar(1) // panic: you cannot reuse p, create new one! |
| 39 | 41 | fmt.Println("finish") |
| 40 | 42 | ``` |
| 6 | 6 | "net/http" |
| 7 | 7 | "os" |
| 8 | 8 | "path/filepath" |
| 9 | "sync" | |
| 9 | 10 | |
| 10 | 11 | "github.com/vbauerster/mpb" |
| 11 | 12 | ) |
| 16 | 17 | url1 := "https://homebrew.bintray.com/bottles/youtube-dl-2016.12.12.sierra.bottle.tar.gz" |
| 17 | 18 | url2 := "https://homebrew.bintray.com/bottles/libtiff-4.0.7.sierra.bottle.tar.gz" |
| 18 | 19 | |
| 20 | var wg sync.WaitGroup | |
| 19 | 21 | p := mpb.New().SetWidth(60) |
| 20 | 22 | |
| 21 | 23 | for i, url := range [...]string{url1, url2} { |
| 22 | p.Wg.Add(1) // if you omit this line, main will return without waiting for download goroutines | |
| 24 | wg.Add(1) | |
| 23 | 25 | name := fmt.Sprintf("url%d:", i+1) |
| 24 | go download(p, name, url) | |
| 26 | go download(&wg, p, name, url) | |
| 25 | 27 | } |
| 26 | 28 | |
| 27 | p.WaitAndStop() | |
| 29 | wg.Wait() | |
| 30 | p.Stop() | |
| 28 | 31 | fmt.Println("Finished") |
| 29 | 32 | } |
| 30 | 33 | |
| 31 | func download(p *mpb.Progress, name, url string) { | |
| 34 | func download(wg *sync.WaitGroup, p *mpb.Progress, name, url string) { | |
| 35 | defer wg.Done() | |
| 32 | 36 | resp, err := http.Get(url) |
| 33 | 37 | if err != nil { |
| 34 | 38 | log.Printf("%s: %v", name, err) |
| 35 | 35 | defer dest.Close() |
| 36 | 36 | |
| 37 | 37 | p := mpb.New().SetWidth(64) |
| 38 | // if you omit the following line, download will complete fine, but rendering bar | |
| 39 | // may not complete, thus better always use even in single thread. | |
| 40 | p.Wg.Add(1) | |
| 41 | 38 | |
| 42 | 39 | bar := p.AddBar(int(size)).PrependCounters(mpb.UnitBytes, 20).AppendETA() |
| 43 | 40 | |
| 47 | 44 | // and copy from reader, ignoring errors |
| 48 | 45 | io.Copy(dest, reader) |
| 49 | 46 | |
| 50 | p.WaitAndStop() | |
| 47 | p.Stop() // if you omit this line, rendering bars goroutine will quit early | |
| 51 | 48 | fmt.Println("Finished") |
| 52 | 49 | } |
| 2 | 2 | import ( |
| 3 | 3 | "fmt" |
| 4 | 4 | "math/rand" |
| 5 | "sync" | |
| 5 | 6 | "time" |
| 6 | 7 | |
| 7 | 8 | "github.com/vbauerster/mpb" |
| 8 | 9 | ) |
| 9 | 10 | |
| 10 | 11 | func main() { |
| 11 | // No need to initialize sync.WaitGroup, as it is initialized implicitly | |
| 12 | p := mpb.New() // Star mpb container | |
| 12 | var wg sync.WaitGroup | |
| 13 | p := mpb.New() // Star mpb's rendering goroutine | |
| 13 | 14 | for i := 0; i < 3; i++ { |
| 14 | p.Wg.Add(1) // add wg counter | |
| 15 | wg.Add(1) // add wg delta | |
| 15 | 16 | name := fmt.Sprintf("Bar#%d:", i) |
| 16 | 17 | bar := p.AddBar(100).PrependName(name, len(name)).AppendPercentage() |
| 17 | 18 | go func() { |
| 19 | defer wg.Done() | |
| 18 | 20 | // you can p.AddBar() here, but ordering will be non deterministic |
| 19 | 21 | for i := 0; i < 100; i++ { |
| 20 | 22 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) |
| 22 | 24 | } |
| 23 | 25 | }() |
| 24 | 26 | } |
| 25 | p.WaitAndStop() // Wait for goroutines to finish | |
| 27 | wg.Wait() // Wait for goroutines to finish | |
| 28 | p.Stop() // Stop mpb's rendering goroutine | |
| 26 | 29 | // p.AddBar(1) // panic: you cannot reuse p, create new one! |
| 27 | 30 | fmt.Println("finish") |
| 28 | 31 | } |
| 30 | 30 | |
| 31 | 31 | // Progress represents the container that renders Progress bars |
| 32 | 32 | type Progress struct { |
| 33 | Wg *sync.WaitGroup | |
| 33 | // WaitGroup for internal rendering sync | |
| 34 | wg *sync.WaitGroup | |
| 34 | 35 | |
| 35 | 36 | out io.Writer |
| 36 | 37 | width int |
| 37 | 38 | sort SortType |
| 38 | // stopped bool | |
| 39 | 39 | |
| 40 | 40 | op chan *operation |
| 41 | 41 | rrChangeReqCh chan time.Duration |
| 59 | 59 | outChangeReqCh: make(chan io.Writer), |
| 60 | 60 | countReqCh: make(chan chan int), |
| 61 | 61 | allDone: make(chan struct{}), |
| 62 | Wg: new(sync.WaitGroup), | |
| 62 | wg: new(sync.WaitGroup), | |
| 63 | 63 | } |
| 64 | 64 | go p.server(cwriter.New(os.Stdout), time.NewTicker(rr*time.Millisecond)) |
| 65 | 65 | return p |
| 98 | 98 | |
| 99 | 99 | // AddBar creates a new progress bar and adds to the container |
| 100 | 100 | func (p *Progress) AddBar(total int) *Bar { |
| 101 | bar := newBar(total, p.width, p.Wg) | |
| 102 | p.op <- &operation{opBarAdd, bar, nil} | |
| 101 | result := make(chan bool) | |
| 102 | bar := newBar(total, p.width, p.wg) | |
| 103 | p.op <- &operation{opBarAdd, bar, result} | |
| 104 | if <-result { | |
| 105 | p.wg.Add(1) | |
| 106 | } | |
| 103 | 107 | return bar |
| 104 | 108 | } |
| 105 | 109 | |
| 117 | 121 | return <-respCh |
| 118 | 122 | } |
| 119 | 123 | |
| 120 | // WaitAndStop stops listening | |
| 121 | func (p *Progress) WaitAndStop() { | |
| 124 | // Stop waits for bars to finish rendering and stops the rendering goroutine | |
| 125 | func (p *Progress) Stop() { | |
| 122 | 126 | if !p.isAllDone() { |
| 123 | 127 | close(p.allDone) |
| 124 | p.Wg.Wait() | |
| 128 | p.wg.Wait() | |
| 125 | 129 | close(p.op) |
| 126 | 130 | } |
| 127 | 131 | } |
| 142 | 146 | switch op.kind { |
| 143 | 147 | case opBarAdd: |
| 144 | 148 | bars = append(bars, op.bar) |
| 149 | op.result <- true | |
| 145 | 150 | case opBarRemove: |
| 146 | 151 | var ok bool |
| 147 | 152 | for i, b := range bars { |
| 164 | 169 | sort.Sort(SortableBarSlice(bars)) |
| 165 | 170 | } |
| 166 | 171 | for _, b := range bars { |
| 167 | // cannot parallel this, because order matters | |
| 168 | 172 | fmt.Fprintln(cw, b) |
| 169 | 173 | } |
| 170 | 174 | cw.Flush() |