Why not all my goroutines executed? Need an explaination

Issue

    func main() {
    var wg sync.WaitGroup
    ch := make(chan int,5)
    start:= time.Now()
    cnt:=0
    wg.Add(10000)
    for i:=0; i<10000; i++ {
        ch <- 1
        go func() {
            defer wg.Done()
            doSomething()
            cnt++
            <-ch
        }()
    }
    wg.Wait()
    fmt.Println(cnt)
    end:= time.Now()
    fmt.Println("End of program.",end.Sub(start))
}

Here I want to execute the program concurrently, and also I want there to be a maximum of 5 goroutines.
The problem is when I print out the "cnt", it won’t be 10000. That means I have some goroutines that were not executed. How can I fix this problem?

Now I am using a mutex to fix the problem, but the run-time of this program won’t be better with the goroutine, I don’t understand why.

    func main() {
    var wg sync.WaitGroup
    var mutex sync.Mutex
    ch := make(chan int,5)
    start:= time.Now()
    cnt:=0
    for i:=0; i<10000; i++ {
        wg.Add(1)
        ch <- 1
        go func() {
            defer wg.Done()
            defer mutex.Unlock()
            mutex.Lock()
            doSomething()
            cnt++
            <-ch
        }()
    }
    wg.Wait()
    fmt.Println(cnt)
    end:= time.Now()
    fmt.Println("End of program.",end.Sub(start))
}

Solution

Try playing with the number of workers in the following program and see which number gives you the best result. Although, the way you’re benchmarking isn’t really reliable and should be usually avoided.

But this program should work better; there are better implementations for sure. So here you just spawning workers amount of goroutines but in your case, it’s 10000 goroutines which is really not necessary and for small cases; it’s an overkill.

Note: For me, this program works >50% better than the implementation that you have.

package main

import (
    "fmt"
    "sync"
    "time"
)

type work struct {
    wg   *sync.WaitGroup
    jobs <-chan struct{}

    mu  *sync.Mutex
    val *int
}

// worker is responsible for executing the work assigned
func worker(w work) {
    for range w.jobs {
        w.mu.Lock()
        *w.val++
        w.mu.Unlock()
    }
    w.wg.Done()
}

func main() {
    start := time.Now()
    jobs := make(chan struct{}, 2) // Number of jobs (buffer)
    workers := 2                   // Number of workers
    cnt := 0                       // Shared variable among workers

    work := work{
        wg:   &sync.WaitGroup{},
        jobs: jobs,
        mu:   &sync.Mutex{},
        val:  &cnt,
    }

    // Worker Pool
    work.wg.Add(workers)
    for i := 0; i < workers; i++ {
        go worker(work)
    }

    // Allocate jobs (Signal worker(s))
    for i := 0; i < 10000; i++ {
        jobs <- struct{}{}
    }
    // Ask the workers to stop
    close(jobs)
    work.wg.Wait()

    fmt.Println(cnt)
    fmt.Println("End of program: ", time.Since(start))
}

Answered By – shmsr

Answer Checked By – Gilberto Lyons (GoLangFix Admin)

Leave a Reply

Your email address will not be published.