Golang:select 语句在不应该退出的时候退出

Golang: select statement exits when it shouldn't

我正在尝试创建一个程序,分别每 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 会永远持续下去,但它不会。

一旦case被执行,select语句就是"done"。如果cases中指定的none个通信操作可以进行没有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.Duration values much easier, using constants from the time 包:

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() 函数类似的任务。

Go 中的

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

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

select 的作用:

  • 如果所有都被阻止,它会等到一个可以继续
  • 如果可以进行多个,则随机选择一个。
  • 当 none 个通道操作可以继续并且存在默认子句时,将执行此操作:默认总是 运行nable(即:准备执行)。

在带有默认大小写的 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