带有通道和等待组的Golang select语句

elcex8rz  于 2023-05-11  发布在  Go
关注(0)|答案(2)|浏览(142)

在对Golang进行实验时,我创建了一个带有select语句的函数,该函数监听两个通道。

我的问题是代码似乎表现得不确定-有时它会恐慌,有时它会成功完成。
**我的期望是这段代码应该总是会死机。**它应该首先接收到错误,因为它应该在waitGroup完成之前被调度,因此在成功通道被推送到之前。

package main

import (
    "errors"
    "fmt"
    "sync"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)

    doSomething(success, errs)

    select {
    case err := <-errs:
        fmt.Println("error", err)
        panic(err)
    case <-success:
        fmt.Println("success")
    }
    fmt.Println("finished successfully")
}

func doSomething(success chan bool, errs chan error) {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        err := errors.New("Some error")
        errs <- err
    }()

    wg.Wait()
    success <- true
}
oogrdqng

oogrdqng1#

两个通道在select语句前都已就绪;所以它将通过均匀伪随机选择来选择:

让我们替换代码中的doSomething函数调用,并将defer放在函数的末尾:

package main

import (
    "errors"
    "fmt"
    "sync"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        err := errors.New("some error")
        errs <- err
        wg.Done()
    }()

    wg.Wait()
    success <- true

    select {
    case err := <-errs:
        fmt.Println("error", err)
        panic(err)
    case <-success:
        fmt.Println("success")
    }
    fmt.Println("finished successfully")
}

正如你在上面的代码示例中看到的,主goroutine在wg.Wait()等待wg.Done()在这个时间点代码(几乎)在功能上等于下面的代码,并且两个通道都准备好了在这里的select语句之前:

package main

import (
    "errors"
    "fmt"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)
    errs <- errors.New("some error")
    success <- true

    select {
    case err := <-errs:
        fmt.Println(err)
    case <-success:
        fmt.Println("success")
    }
}

运行:

$ go run .
some error

$ go run .
success

Select语句(_S):
如果一个或多个通信可以继续进行,则通过均匀伪随机选择**选择可以继续进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,则“select”语句阻塞,直到至少一个通信可以继续。

uqcuzwp8

uqcuzwp82#

我认为你应该把select放到一个go例程中。像这样:

package main

import (
    "errors"
    "fmt"
    "sync"
)

func main() {
    errs := make(chan error, 1)
    success := make(chan bool, 1)
    go func() {
        select {
        case err := <-errs:
            fmt.Println("error", err)
            panic(err)
        case <-success:
            fmt.Println("success")
        }
        fmt.Println("finished successfully")
    }()

    doSomething(success, errs)
}

func doSomething(success chan bool, errs chan error) {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        err := errors.New("Some error")
        errs <- err
    }()

    wg.Wait()
    success <- true
}

相关问题