How does channel blocking work in Go?

huangapple go评论144阅读模式
英文:

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 &quot;time&quot;
import &quot;fmt&quot;
func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    go func() {
        time.Sleep(time.Second * 1)
        c1 &lt;- &quot;one&quot;
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 &lt;- &quot;two&quot;
    }()
    for i := 0; i &lt; 2; i++ {
      select {
        case msg1 := &lt;-c1:
          fmt.Println(&quot;received&quot;, msg1)
        case msg2 := &lt;-c2:
          fmt.Println(&quot;received&quot;, msg2)
        default:
          fmt.Println(&quot;Default&quot;)
      }
    }
}

Output:

Default
Default
Program Exited

If I comment out the default section

//default:
//    fmt.Println(&quot;Default&quot;)

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语句的工作方式有关。

根据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"语句将阻塞,直到至少有一个通信可以进行。

在原始代码中,由于在c1c2上发送任何数据之前存在延迟,所以default case在循环的两次迭代中都准备就绪。

在删除default case之后,select语句必须等待c1c2中有数据可用。

英文:

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 &lt;- &quot;one&quot;
    }()
// 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 &lt;- &quot;one&quot;// value &quot;one&quot; is passed to this channel.

select statement: Here it waits for the goroutines to complete it&#39;s task. 
Once a goroutine above finishes, it matches it&#39;s case and executes the 
statements. 

huangapple
  • 本文由 发表于 2015年9月12日 19:50:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/32538438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定