登录
首页 >  Golang >  Go问答

goroutine 中使用 select 语句进行通道通信行为

来源:stackoverflow

时间:2024-04-14 15:48:37 342浏览 收藏

本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《goroutine 中使用 select 语句进行通道通信行为》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~

问题内容

这是“围棋之旅”提供的版本的修改版。

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

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


解决方案


因为 goroutine 中的接收者 (go func() {...}) 在接收来自发送者的值时并发执行(func fibonacci() 中的 select 语句)。

通过在发送值后添加延迟(c <- x:)将避免同时发送多个数据,以便您看得更清楚。 试试这个代码:(https://play.golang.org/p/9rOdS2YThKR)

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

这与运行时调度 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.org/p/QD5kyGXWoJk

通过在通道 c 上的每次发送后添加延迟,我们调用 goroutine 调度程序将当前 goroutine 放入等待队列,从而允许另一个 goroutine 被调度并成功打印消息。 golang具有协作调度,因此goroutine不会立即被抢占。

今天带大家了解了的相关知识,希望对你有所帮助;关于Golang的技术知识我们会一点点深入介绍,欢迎大家关注golang学习网公众号,一起学习编程~

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>