英文:
How does channel blocking work in Go?
问题
我正在学习Go语言。这里有一个我遇到的例子,请有人解释一下这里发生了什么?
package main
import "time"
import "fmt"
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
default:
fmt.Println("Default")
}
}
}
输出结果:
Default
Default
程序退出
如果我注释掉default
部分:
//default:
// fmt.Println("Default")
输出结果变为:
received one
received two
程序退出
default
语句的存在如何改变通道阻塞的工作方式?
英文:
I'm learning the Go language. Here is an example I've come across. Can someone please explain what is happening here?
package main
import "time"
import "fmt"
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
default:
fmt.Println("Default")
}
}
}
Output:
Default
Default
Program Exited
If I comment out the default section
//default:
// fmt.Println("Default")
the output becomes:
received one
received two
Program exited.
How does the presence of the default
case change the way channel blocking works?
答案1
得分: 38
这与Go语言中的select
语句的工作方式有关。
> 如果有一个或多个通信可以进行,那么通过均匀的伪随机选择选择一个可以进行的通信。否则,如果有一个默认情况,就选择该情况。如果没有默认情况,"select"语句将阻塞,直到至少有一个通信可以进行。
因此,如果没有默认情况,代码将阻塞,直到任一通道中有数据可用。它会隐式等待其他goroutine唤醒并写入它们的通道。
当添加了默认情况时,很可能在其他goroutine从休眠中唤醒之前就会执行select
语句。
因此,由于尚无可用数据,并且存在默认情况,将执行默认情况。这会发生两次,并且耗时不到1秒。因此,在任何goroutine有机会唤醒并写入通道之前,程序就会终止。
请注意,这在技术上是一种竞态条件;无法保证循环的两次迭代在任何goroutine唤醒之前运行,因此理论上即使有默认情况,也有可能产生不同的输出,但在实践中这种情况极不可能发生。
英文:
This is related to how select
statements work in Go.
From the Go documentation on select
:
> If one or more of the communications can proceed, a single one that
> can proceed is chosen via a uniform pseudo-random selection.
> Otherwise, if there is a default case, that case is chosen. If there
> is no default case, the "select" statement blocks until at least one
> of the communications can proceed.
So, without a default case, the code will block until some data is available in either of the channels. It implicitly waits for the other goroutines to wake up and write to their channel.
When you add the default case, it is very likely that the select
statement is reached before the other goroutines wake up from sleeping.
So, since there is no data available (yet), and there is a default case, the default case is executed. This is done twice, and it takes less than 1 second. So the program ends up terminating before any of the go routines have a chance to wake up and write to the channel.
Note that this is technically a race condition; there is absolutely no guarantee that the 2 iterations of the loop will run before any of the go routines wake up, so in theory it is possible to have a different output even with a default case, but in practice it is extremely unlikely.
答案2
得分: 9
select
语句会阻塞,直到至少有一个case准备就绪。Go语言规范的一部分如下所述:
> 如果有一个或多个通信可以进行,将通过均匀的伪随机选择来选择一个可以进行的通信。否则,如果有一个默认的case,那个case将被选择。如果没有默认的case,"select"语句将阻塞,直到至少有一个通信可以进行。
在原始代码中,由于在c1
或c2
上发送任何数据之前存在延迟,所以default
case在循环的两次迭代中都准备就绪。
在删除default
case之后,select
语句必须等待c1
或c2
中有数据可用。
英文:
The select
statement blocks until at least one case is ready. The Go language specification reads, in part:
> If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
In the original code, the default
case is ready on both iterations of the loop because there is a delay before anything is sent on c1
or c2
.
After you remove the default
case, the select
statement must wait for data to be available in c1
or c2
.
答案3
得分: -3
以下是翻译好的内容:
请参考上述链接以获取示例执行。如果没有其他 case 准备好,将执行默认 case。在 Go 语言中,select 语句会阻塞,直到其中一个 case 准备就绪。因此,移除默认 case 可以使其他 case 的执行成为可能,否则默认 case 会在其他 case 之前准备就绪。
英文:
https://tour.golang.org/concurrency/5
https://tour.golang.org/concurrency/6
See the links given above for example execution. The default case gets executed if no other case is ready.
select in golang blocks until one of the cases is ready. Therefore removing default made the execution of other cases possible otherwise it was the one that was ready before others
答案4
得分: -4
解释:
c1 := make(chan string) // 创建一个字符串类型的通道。[意味着只能在该通道上发送/接收字符串]
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
// func() 是一个 goroutine [由于在前面放置了 go 关键字,如果没有 go 关键字,那么它就是一个普通函数]。
time.Sleep(time.Second * 1) // 在将值传递给通道 c1 之前,此 func() goroutine 将休眠一秒钟。
c1 <- "one" // 将值 "one" 传递给该通道。
select 语句:在这里,它等待 goroutine 完成其任务。一旦上面的 goroutine 完成,它就会匹配相应的 case 并执行相应的语句。
英文:
Explanation:
c1 := make(chan string) // Creates a channel of type string. [Means only
strings can be sent/received on this channel]
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
// func() is a goroutine [As go keyword is placed before, if no go keyword
here, then it is a simple function].
time.Sleep(time.Second * 1) // here this func() goroutine will sleep for a
second before passing a value to the channel c1.
c1 <- "one"// value "one" is passed to this channel.
select statement: Here it waits for the goroutines to complete it's task.
Once a goroutine above finishes, it matches it's case and executes the
statements.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论