Search code examples
gogoroutine

goruntine not running concurrently?


I have the following program, I am new to gorountine, what I want to test is simple, I am calling a gorountine in a loop 100 times, it there is one time failure, the entire program fails, otherwise succeeds, and fail10Percent it delays 1 second, and check a random number if it is 4, let it fail.

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func fail10Percent(ch chan int) {
    time.Sleep(1 * time.Second)
    e := rand.Intn(10)
    fmt.Println("Calculating rand.Intn(10) ", e)

    if e == 4 {
        ch <- 0
        return
    }
    ch <- 1
}

func test() {
    for i := 0; i < 100; i++ {
        err := make(chan int)
        go fail10Percent(err)

        res := <-err

        fmt.Println("=== result: ", res)

        if res != 1 {
            fmt.Println("failed")
            return
        }
    }
    fmt.Println("succeeded")
}

func main() {
    test()
}

I expect the go fail10Percent(err) will run concurrently 100 times, which will only have 1 second delay, however, when I run it, I see the following result getting printed 1 second after 1 second, why is that, and how I can adjust my program to do what I want.

Calculating rand.Intn(10)  1
=== result:  1
Calculating rand.Intn(10)  7
=== result:  1
Calculating rand.Intn(10)  7
=== result:  1
Calculating rand.Intn(10)  9
=== result:  1
Calculating rand.Intn(10)  1
=== result:  1
Calculating rand.Intn(10)  8
=== result:  1
Calculating rand.Intn(10)  5
=== result:  1
Calculating rand.Intn(10)  0
=== result:  1
Calculating rand.Intn(10)  6
=== result:  1
Calculating rand.Intn(10)  0
=== result:  1
Calculating rand.Intn(10)  4
=== result:  0
failed

Solution

  • I've commented out the code for you so that you can understand.

    package main
    
    import (
        "fmt"
        "math/rand"
        "sync"
    )
    
    func fail10Percent(ch chan int, w *sync.WaitGroup) {
        defer w.Done()
        num := rand.Intn(10)
        fmt.Println("calculating rand.Intn(10) ", num)
        if num == 4 {
            ch <- 0 // Fail
            return
        }
        ch <- 1 // Pass
    }
    
    func test() {
        var ch = make(chan int, 1)
        // Launch the receiver goroutine to listen if goroutine succeeded or failed based on the value sent to ch
        go func() {
            for recv := range ch {
                switch recv {
                // Fail
                case 0:
                    fmt.Println("goroutine failed")
                // Pass
                case 1:
                    fmt.Println("goroutine succeed")
                }
            }
        }()
        // wg is a WaitGroup
        var wg sync.WaitGroup
        for i := 0; i < 100; i++ {
            wg.Add(1)
            go fail10Percent(ch, &wg)
        }
        // wg.Wait() to wait for all goroutines to complete
        wg.Wait()
        // Close the channel so that the receiver can stop
        close(ch)
    }
    
    func main() {
        test()
    }
    

    Update:

    Simple solution without using sync.WaitGroup

    package main
    
    import (
        "fmt"
        "math/rand"
    )
    
    // Using a send only channel
    func fail10Percent(ch chan<- int) {
        num := rand.Intn(10)
        fmt.Println("calculating rand.Intn(10) ", num)
        if num == 4 {
            ch <- 0 // Fail
            return
        }
        ch <- 1 // Pass
    }
    
    func test() {
        var ch = make(chan int, 1)
        for i := 0; i < 100; i++ {
            go fail10Percent(ch)
        }
        for i := 0; i < 100; i++ {
            if recv := <-ch; recv == 0 {
                fmt.Println("goroutine failed")
            } else if recv == 1 {
                fmt.Println("goroutine succeed")
            }
        }
        close(ch)
    }
    
    func main() {
        test()
    }