在goroutines中使用select语句的通道通信行为。

问题描述 投票:0回答:1

这是一个修改过的版本,由以下人员提供 "围棋之旅".

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
            fmt.Println("GEN", x)
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println("DISP", <-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

下面是上述代码的输出。

DISP 0
GEN 1
GEN 1
DISP 1
DISP 1
GEN 2
GEN 3
DISP 2
DISP 3
GEN 5
GEN 8
DISP 5
DISP 8
GEN 13
GEN 21
DISP 13
DISP 21
GEN 34
GEN 55
DISP 34
quit

我不明白这段代码的行为。为什么在显示两个斐波那契数之前生成2个斐波那契数?这是否取决于执行环境?

go channel goroutine
1个回答
1
投票

因为在goroutine中的接收器(go func() {...}),当从发送者接收到值时,会同时执行(select语句在 func fibonacci()).

通过在发送后加一个延时值(c <- x:)会避免多人同时发送,让你看得更清楚,试试这段代码。(https:/play.golang.orgp9rOdS2YThKR。)

package main

import (
    "fmt"
    "time"
)

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        fmt.Println("-----------------------------------------")
        fmt.Println("current x:", x)
        select {
        //Send a value into a channel using the c <- x syntax, block until receiver is ready
        case c <- x:
            //When receiver gets x value, this code will executes
            //Delay, avoid mutilple sending at the same time
            time.Sleep(5 * time.Millisecond)
            //Increase
            x, y = y, x+y
            fmt.Println("increased x to", x)
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        //The <-c syntax receives a value from the channel, block until sender is ready
        for i := 0; i < 10; i++ {
            fmt.Println("received x:", <-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

上述代码的结果是。

-----------------------------------------
current x: 0
received x: 0
increased x to 1
-----------------------------------------
current x: 1
received x: 1
increased x to 1
-----------------------------------------
current x: 1
received x: 1
increased x to 2
-----------------------------------------
current x: 2
received x: 2
increased x to 3
-----------------------------------------
current x: 3
received x: 3
increased x to 5
-----------------------------------------
current x: 5
received x: 5
increased x to 8
-----------------------------------------
current x: 8
received x: 8
increased x to 13
-----------------------------------------
current x: 13
received x: 13
increased x to 21
-----------------------------------------
current x: 21
received x: 21
increased x to 34
-----------------------------------------
current x: 34
received x: 34
increased x to 55
-----------------------------------------
current x: 55
quit

1
投票

这与运行时如何调度goroutine有关。首先通过在每次推送通道c后增加一个延迟,我们得到正确的结果,如下图所示。

package main

import (
    "fmt"
    "time"
)

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            time.Sleep(1 * time.Millisecond)
            x, y = y, x+y
            fmt.Println("GEN", x)
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println("DISP", <-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}

以上代码的结果是:

DISP 0
GEN 1
DISP 1
GEN 1
DISP 1
GEN 2
DISP 2
GEN 3
DISP 3
GEN 5
DISP 5
GEN 8
DISP 8
GEN 13
DISP 13
GEN 21
DISP 21
GEN 34
DISP 34
GEN 55
quit

去游乐场的链接: https:/play.golang.orgpQD5kyGXWoJk

通过在通道c上每次发送后增加一个延迟,我们是调用goroutine调度器将当前goroutine放在等待队列中,从而让其他goroutine被调度并成功打印消息。Golang有合作调度,所以goroutine不会立即被抢占。

© www.soinside.com 2019 - 2024. All rights reserved.