Golang的select/非缓冲的Channel实例详解

与操作缓冲通道的select相比,它被阻塞的概率一般会大很多。只有存在可配对的操作的时候,传递元素值的动作才能真正的开始。
示例:

发送操作间隔1s,接收操作间隔2s
分别向unbufChan通道发送小于10和大于等于10的整数,这样更容易从打印结果分辨出配对的时候哪一个case被选中了。下列案例两个case是被随机选择的。

package main

import (
    "fmt"
    "time"
)

func main(){
    unbufChan := make(chan int)
    sign := make(chan byte, 2)

    go func(){
        for i := 0; i < 10; i++ {
            select {
                case unbufChan <- i:
                case unbufChan <- i + 10:
                default:
                    fmt.Println("default!")
            }
            time.Sleep(time.Second)
        }
        close(unbufChan)
        fmt.Println("The channel is closed.")
        sign <- 0
    }()

    go func(){
        loop:
            for {
                select {
                    case e, ok := <-unbufChan:
                    if !ok {
                        fmt.Println("Closed channel.")
                        break loop
                    }
                    fmt.Printf("e: %d
",e)
                    time.Sleep(2 * time.Second)
                }
            }
            sign <- 1
    }()
    <- sign
    <- sign
}

default! //无法配对
e: 1
default!//无法配对
e: 3
default!//无法配对
e: 15
default!//无法配对
e: 17
default!//无法配对
e: 9
The channel is closed.
Closed channel.

default case会在收发操作无法配对的情况下被选中并执行。在这里它被选中的概率是50%。

  • 上面的示例给予了我们这样一个启发:使用非缓冲通道能够让我们非常方便地在接收端对发送端的操作频率实施控制。
  • 可以尝试去掉default case,看看打印结果,代码稍作修改如下:
package main

import (
    "fmt"
    "time"
)

func main(){
    unbufChan := make(chan int)
    sign := make(chan byte, 2)

    go func(){
        for i := 0; i < 10; i++ {
            select {
                case unbufChan <- i:
                case unbufChan <- i + 10:

            }
            fmt.Printf("The %d select is selected
",i)
            time.Sleep(time.Second)
        }
        close(unbufChan)
        fmt.Println("The channel is closed.")
        sign <- 0
    }()

    go func(){
        loop:
            for {
                select {
                    case e, ok := <-unbufChan:
                    if !ok {
                        fmt.Println("Closed channel.")
                        break loop
                    }
                    fmt.Printf("e: %d
",e)
                    time.Sleep(2 * time.Second)
                }
            }
            sign <- 1
    }()
    <- sign
    <- sign
}

e: 0
The 0 select is selected
e: 11
The 1 select is selected
e: 12
The 2 select is selected
e: 3
The 3 select is selected
e: 14
The 4 select is selected
e: 5
The 5 select is selected
e: 16
The 6 select is selected
e: 17
The 7 select is selected
e: 8
The 8 select is selected
e: 19
The 9 select is selected
The channel is closed.
Closed channel.

总结:上面两个例子,第一个有default case 无法配对时执行该语句,而第二个没有default case ,无法配对case时select将阻塞,直到某个case可以运行(上述示例是直到unbufChan数据被读取操作),不会重新对channel或值进行求值。

文章导航