Golang:select语句不应该退出

时间:2016-03-31 07:55:29

标签: select go channel channels

我正在尝试创建一个程序,分别每隔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语句将永远存在。为什么现在不呢?

2 个答案:

答案 0 :(得分:2)

我不知道你在哪里读到select永远存在,但事实并非如此。

执行case后,select语句“完成”。如果case中指定的任何通信操作都无法继续,则没有default分支,select将阻止,只要任何一个com 。操作可以继续。但是,执行case后,select不会重复。

阅读规范中的相关部分: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包中的常量,您可以更轻松地创建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()功能类似的任务。

答案 1 :(得分:2)

Go中的

select大部分类似于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