错误的使用方法

package main
// 错误使用案例
import (
    "time"
    "fmt"
)
var c1 chan string = make(chan string)
func main(){
    func(){
       time.Sleep(time.Second * 2)
        c1 <- "result 1"
    }()
    fmt.Println("c1 is", <-c1)
}
c1c1c1c1
fmt.Println("c1 is", <-c1)

这段代码本来是可以阻塞的,但是这种用法是错误的

正确的使用方法 (将写入放到单独的协程中)

通过将写入端放到单独的协程中,使得channel 的写入端和接收端可以对接。

// ....
func main(){
    go func(){
       time.Sleep(time.Second * 2)
        c1 <- "result 1"
    }()
    fmt.Println("I am here")
    fmt.Println("c1 is", <-c1)
}

可以看到 匿名函数部分代码是在独立的协程中执行的,在同一个协程中对一个channel写入时,如果channel 没有缓存长度,必然会报错,见错误案例

select 用法

select 可以同时监听多个channel

阻塞方式的

执行到select 语句时如果所有的case 中的channel 都没有数据,select 语句将会阻塞,直到一个case中的 channel 获取到数据。

package main
import (
    "time"
    "fmt"
)

func AskX(bid1 chan int) {
    for i:=100 ;i<105 ; i++{
        time.Sleep(time.Second*1)
        bid1 <- i
    }
}
func AskY(bid2 chan int) {
    for i:=0 ; i<5; i++{
        time.Sleep(time.Second*2)
        bid2 <- i
    }

}

func main(){
    bid1 := make(chan int)
    bid2 := make(chan int)

    go AskX(bid1)
    go AskY(bid2)

    select {
    case Xbid := <-bid1:
        fmt.Println(Xbid)
    case Ybid := <-bid2:
        fmt.Println(Ybid)
    }
}
100bid1

不阻塞的

将阻塞部分 select 处代码修改为

    select {
    case Xbid := <-bid1:
        fmt.Println(Xbid)
    case Ybid := <-bid2:
        fmt.Println(Ybid)
    default :
        fmt.Println("no data.")
    }

defaultdefault

带超时机制的

   select {
   case Xbid := <-bid1:
       fmt.Println(Xbid)
   case Ybid := <-bid2:
       fmt.Println(Ybid)
   case <-time.After(time.Second*2):
       fmt.Println("overtime for 2 seconds")
   }

上面的代码表示,当所有channel 超过2秒还没有取到数据,就会执行超时设置部分的代码。

总结

  • 在同一个协程中如果对一个channel写入数据,如果channel 没有缓存长度,必然报错

  • 在主协程中有接收端,在子协程中没有发送端会报错

  • 在子协程中有发送端,在其他协程中没有接收端,不会报错