goruntine 不是 运行 并发?

goruntine not running concurrently?

我有下面这个程序,我刚接触gorountine,我想测试的很简单,我循环调用一个gorountine 100次,有一次失败,整个程序失败,否则成功, 并且fail10Percent 延迟1秒, 并检查一个随机数如果是4, 就让它失败。

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()
}

我预计 go fail10Percent(err) 会并发 运行 100 次,这只会有 1 秒的延迟,但是,当我 运行 它时,我看到打印了以下结果 1 1 秒后秒,这是为什么,以及我如何调整我的程序来做我想做的事情。

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

我把代码注释掉了,方便你理解。

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()
}

更新:

不使用 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()
}