【问题标题】:Golang: select statement exits when it shouldn'tGolang:select语句在不应该退出时退出
【发布时间】:2016-07-19 13:12:14
【问题描述】:

我正在尝试创建一个程序,分别每 3、8 和 24 秒打印一次"Eat""Work""Sleep"。这是我的代码:

package main

import (
"fmt"
"time"
)

func Remind(text string, delay time.Duration) <-chan string { //channel only for receiving strings
    ch := make(chan string) // buffered/unbuffered?
    go func() {
        for {
            msg := "The time is " + time.Now().Format("2006-01-02 15:04:05 ") + text
            ch <- msg
            time.Sleep(delay) // waits according to specification
        }
    }()
    return ch
}

func main() {
    ch1 := Remind("Eat", 1000*1000*1000*3) // every third second    
    ch2 := Remind("Work", 1000*1000*1000*8) // every eighth second
    ch3 := Remind("Sleep", 1000*1000*1000*24) // every 24th second
    select { // chooses one channel that is not empty. Should run forever (?)
        case rem1 := <-ch1:
            fmt.Println(rem1)
        case rem2 := <-ch2:
            fmt.Println(rem2)
        case rem3 := <-ch3:
            fmt.Println(rem3)
    }
}

它的问题是它在打印时间后立即停止运行,然后是"Eat"。在我读过的其他示例中,select 语句永远存在。为什么现在不行了?

【问题讨论】:

    标签: select go channel channels


    【解决方案1】:

    我不知道你在哪里读到select 会永远持续下去,但事实并非如此。

    一旦执行了caseselect 语句就“完成”了。如果cases 中指定的通信操作都不能进行并且没有default 分支,select 将阻塞,只要任何 com.操作可以继续。但是一旦执行了caseselect 就不会重复。

    阅读规范中的相关部分:Select statements

    把它放在一个无穷无尽的for 中让它永远重复:

    for {
        select { // chooses one channel that is not empty. Should run forever (?)
        case rem1 := <-ch1:
            fmt.Println(rem1)
        case rem2 := <-ch2:
            fmt.Println(rem2)
        case rem3 := <-ch3:
            fmt.Println(rem3)
        }
    }
    

    附注:

    您可以使用time 包中的常量更轻松地创建time.Duration 值:

    ch1 := Remind("Eat", 3*time.Second)    // every third second
    ch2 := Remind("Work", 8*time.Second)   // every eighth second
    ch3 := Remind("Sleep", 24*time.Second) // every 24th second
    

    您可能还想查看 time.Ticker 类型,该类型用于类似于您的 Remind() 函数的任务。

    【讨论】:

    • 那一定是误会了什么。谢谢!
    • @Sandi 如果通信操作无法继续,select 很可能会等待,如果需要则永远等待。但它不会重复。
    • 所以你的意思是,例如,如果所有频道都是空的,select 会等待吗?
    • @Sandi 是的,类似的。例如select{} 永远阻塞。
    【解决方案2】:

    select 在 Go 中最类似于​​ switch 控制语句,有时也称为通信开关。 select 侦听通道上的传入数据,但也可能存在在通道上发送值的情况。总之,select 用于在并发执行的 goroutine 上获取或发送值。

    在您的示例中,因为您在主 goroutine 中执行当前时间,所以它总是被执行。但是因为其他 goroutine 是在 select 语句中执行的,所以它们并不总是有机会被执行,因为一旦执行了 case,通道就会阻塞。

    选择的作用:

    • 如果所有都被阻止,它会等待直到可以继续
    • 如果可以进行多个,则随机选择一个。
    • 当没有任何通道操作可以继续并且存在默认子句时,则执行以下操作:默认始终可运行(即:准备执行)。

    在带有默认情况的 select 语句中使用发送操作可以保证发送是非阻塞的!

    要在 for 循环中永久运行:

    package main
    
    import (
    "fmt"
    "time"
    )
    
    func Remind(text string, delay time.Duration) <-chan string { //channel only for receiving strings
        ch := make(chan string) // buffered/unbuffered?
        go func() {
            for {
                msg := "The time is " + time.Now().Format("2006-01-02 15:04:05 ") + text
                ch <- msg
                time.Sleep(delay) // waits according to specification
            }
        }()
        return ch
    }
    
    func main() {
        ch1 := Remind("Eat", 1000*1000*1000*3) // every third second    
        ch2 := Remind("Work", 1000*1000*1000*8) // every eighth second
        ch3 := Remind("Sleep", 1000*1000*1000*24) // every 24th second
        for {
            select { // chooses one channel that is not empty. Should run forever (?)
            case rem1 := <-ch1:
                fmt.Println(rem1)
            case rem2 := <-ch2:
                fmt.Println(rem2)
            case rem3 := <-ch3:
                fmt.Println(rem3)
            }
        }
    }
    

    http://play.golang.org/p/BuPqm3xsv6

    【讨论】:

    • 感谢您的回答。通道阻塞是什么意思?我很难找到它的定义。
    猜你喜欢
    • 1970-01-01
    • 2014-04-12
    • 2017-02-09
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    • 1970-01-01
    • 2015-10-08
    相关资源
    最近更新 更多