英文:
Golang: select statement exits when it shouldn't
问题
我正在尝试创建一个程序,每隔第3秒、第8秒和第24秒分别打印出"吃饭"、"工作"和"睡觉"。以下是我的代码:
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("吃饭", 1000*1000*1000*3) // 每隔三秒
ch2 := Remind("工作", 1000*1000*1000*8) // 每隔八秒
ch3 := Remind("睡觉", 1000*1000*1000*24) // 每隔24秒
select { // 选择一个非空的通道。应该无限循环运行 (?)
case rem1 := <-ch1:
fmt.Println(rem1)
case rem2 := <-ch2:
fmt.Println(rem2)
case rem3 := <-ch3:
fmt.Println(rem3)
}
}
问题在于,在打印出时间后紧接着的"吃饭"之后,程序立即停止运行。在我阅读的其他示例中,select
语句会一直运行。为什么这次不会呢?
英文:
I'm trying to create a program that prints "Eat"
, "Work"
, "Sleep"
every 3rd, 8th, and 24th second respectively. Here is my code:
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)
}
}
The problem with it is that it stops running immediately after printing the time followed by "Eat"
. In other examples I have read, the select
statement goes on forever. Why doesn't it now?
答案1
得分: 4
我不知道你在哪里看到select
会无限循环的说法,但实际上并不是这样的。
一旦执行了一个case
,select
语句就会结束。如果在case
中没有任何通信操作可以执行,并且没有default
分支,select
会阻塞,直到任何一个通信操作可以执行为止。但是一旦执行了一个case
,select
就不会重复执行。
从规范中阅读相关部分:Select语句。
你可以将它放在一个无限循环的for
中,使其无限重复:
for {
select { // 选择一个非空的通道。应该会无限循环(?)
case rem1 := <-ch1:
fmt.Println(rem1)
case rem2 := <-ch2:
fmt.Println(rem2)
case rem3 := <-ch3:
fmt.Println(rem3)
}
}
另外注意:
你可以更轻松地创建time.Duration
值,使用time
包中的常量:
ch1 := Remind("吃饭", 3*time.Second) // 每3秒钟提醒一次
ch2 := Remind("工作", 8*time.Second) // 每8秒钟提醒一次
ch3 := Remind("睡觉", 24*time.Second) // 每24秒钟提醒一次
你可能还想了解一下time.Ticker
类型,它用于类似于你的Remind()
函数的任务。
英文:
I don't know where you've read that the select
goes on forever, but it doesn't.
Once a case
is executed, the select
statement is "done". If none of the communication operations specified in case
s can proceed and there is no default
branch, select
will block, for as long as any of the com. ops can proceed. But once a case
is executed, select
does not repeat.
Read the relevant section from the spec: Select statements.
Put it in an endless for
to make it repeat forever:
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)
}
}
As a sidenote:
You can create time.Duration
values much easier, using constants from the time
package:
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
You may also want to check out the time.Ticker
type which is for tasks similar to your Remind()
function.
答案2
得分: 2
在Go语言中,select
语句与switch
控制语句非常相似,有时也被称为通信开关。select
用于监听通道上的传入数据,但也可能存在在通道上发送值的情况。简而言之,select
用于在并发执行的goroutine中获取或发送值。
在你的示例中,因为你在主goroutine中执行当前时间,所以它总是被执行。但是,由于其他goroutine在select
语句中执行,它们并不总是有机会被执行,因为一旦执行了一个case
,通道就会阻塞。
select
的作用:
- 如果所有通道都被阻塞,它会等待直到有一个通道可以继续执行。
- 如果有多个通道可以继续执行,它会随机选择一个。
- 当所有通道操作都无法继续执行且存在
default
子句时,会执行default
子句:default
始终可运行(即准备执行)。
在select
语句中使用发送操作和default
子句可以保证发送操作是非阻塞的!
要无限循环地运行,可以将其放在一个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) // 每3秒钟执行一次
ch2 := Remind("Work", 1000*1000*1000*8) // 每8秒钟执行一次
ch3 := Remind("Sleep", 1000*1000*1000*24) // 每24秒钟执行一次
for {
select { // 选择一个非空的通道。应该无限循环运行(?)
case rem1 := <-ch1:
fmt.Println(rem1)
case rem2 := <-ch2:
fmt.Println(rem2)
case rem3 := <-ch3:
fmt.Println(rem3)
}
}
}
你可以在这里运行代码:http://play.golang.org/p/BuPqm3xsv6
英文:
select
in Go mostly resembles the switch control statement and is sometimes called communication switch. select
listens for incoming data on channels, but there could also be cases where a value is sent on a channel. In one word select
is used to get or send values on concurrently executing goroutines.
In your example because you are executing the current time in the main goroutine, it's always executed. But because the other goroutines are executed in the select statement these are not always get the chance to be executed, because once a case
is executed the channel blocks.
What select does:
- if all are blocked, it waits until one can proceed
- if multiple can proceed, it chooses one at random.
- when none of the channel operations can proceed and the default clause is present, then this is executed: the default is always runnable (that is: ready to execute).
Using a send operation in a select statement with a default case guarantees that the send will be non-blocking!
To run forever use it in a for loop:
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)
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论