3 ms·
My personal style here is to use a number of goroutines equal to the desired concurrency. I know goroutines are cheap and there is no particular reason to conse
by klodolph 6y ago
My personal style here is to use a number of goroutines equal to the desired concurrency. I know goroutines are cheap and there is no particular reason to conserve them most of the time, but this feels more natural to me than using a semaphore--although it is less flexible.
Simplified:
var wg sync.WaitGroup
wg.Add(jobs)
ch := make(chan workItem, jobs)
for i := 0; i < jobs; i++ {
go func() {
defer wg.Done()
for item := range ch {
processItem(item)
}
}()
}
for _, item := range items {
ch <- item
}
close(ch)
wg.Wait()
- andonisus 6y agoHere's how I like to do it: https://play.golang.com/p/AB6exUIkNRg https://play.golang.com/p/AB6exUIkNRg Contents pasted below. package main import ( "fmt" "sync" ) func main() { wg := &sync.WaitGroup{} e := NewParallelExecutor(uint64(10)) for i := 0; i < 100; i++ { wg.Add(1) func(_i int) { //make closure to capture i correctly e.Submit(func() { fmt.Println(_i) wg.Done() }) }(i) } wg.Wait() fmt.Println("All done.") } type Executor interface { Submit(func()) } type parallelExecutor struct { queue []func() inCh chan func() doneCh chan struct{} } func NewParallelExecutor(limit uint64) Executor { pe := ¶llelExecutor{ queue: make([]func(), 0, limit), inCh: make(chan func()), doneCh: make(chan struct{}), } go func() { running := uint64(0) for { select { case <- pe.doneCh: running -= 1 case f := <- pe.inCh: pe.queue = append(pe.queue, f) } if len(pe.queue) > 0 && running < limit { running += 1 fmt.Println(fmt.Sprintf("Goroutines in flight, waiting: %d | %d", running, len(pe.queue))) f := pe.queue[0] pe.queue = pe.queue[1:] go func(_f func()) { _f() pe.doneCh <- struct{}{} }(f) } } }() return pe } func (p *parallelExecutor) Submit(f func()) { p.inCh <- f }