channel

channel基本用法

# 使用通道
ch <- x                        // 发送语句
x := <-ch                      // 赋值语句
<-ch                           // 接收语句,丢弃结果

# 创建通道
ch := make(chan int)           // 无缓冲通道
ch := make(chan int, 0)        // 无缓冲通道
ch := make(chat int, 3)        // 容量为3的缓冲通道

应用场景:

  • 无缓冲通道:用于发送方协程和接收方协程同步化
  • 有缓冲通道:提高性能

阻塞场景

无缓冲通道

特点,发送的数据被接收了,才算完成。
阻塞场景:

  • 通道中无数据,向通道写数据,但没有其它携程读取
  • 通道中无数据,向通道读数据

例子:

// 情形一
func main()  {
    c := make(chan string)        
    c <- "hello world"           // 执行到该步会一直阻塞,因为接收方和发送发是同一个携程,始终没有接收方接收,进而导致报错
    msg := <- c                  
}

// 情形二
func main()  {
    c := make(chan string)
    fmt.Println(<-c)
}

// output
fatal error: all goroutines are asleep - deadlock!

有缓冲通道

特点:有缓存空间时,发送方会向通道发送内容然后直接返回(即非阻塞),缓存中有数据时从通道中读取数据直接返回
阻塞场景:

  • 通道已满,执行写通道,但无携程读。
  • 通道缓存无数据,但执行读通道。
// 情形一
func main()  {
    c := make(chan string, 2)
    c <- "hello"
    c <- "world"
    c <- "golang"

    fmt.Println(<-c)
}

// 情形二
func main()  {
    c := make(chan string, 2)

    fmt.Println(<-c)
}

# output
fatal error: all goroutines are asleep - deadlock!

非阻塞写法

无缓冲通道

方式1:先写接受者

func main()  {
    c := make(chan string)

    // 接收者
    go func() {
        v := <-c
        fmt.Println(v)
    }()

    // 发送者
    c <- "pig"
}

方式2:先写发送者

func main()  {
    c := make(chan string)

    // 发送者
    go func() {
        c <- "pig"
    }()
    
    // 接收者
    fmt.Println(<-c)
}

有缓冲通道

使用select解决多个协程读通道阻塞问题

func main()  {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {                        // 协程1
        for {
            c1 <- "Event 1s"
            time.Sleep(1 * time.Second)
        }
    }()

    go func() {                        // 协程2
        for {
            c2 <- "Event 2s"
            time.Sleep(2 * time.Second)
        }
    }()

    for {
        // fmt.Println(<-c1)            // 如果c1读通道被阻塞,则导致c2执行不了读通道
        // fmt.Println(<-c2)            // 同理如上
        select {
        case msg1 := <-c1:
            fmt.Println(msg1)            // case默认break
        case msg2 := <-c2:
            fmt.Println(msg2)
        }
    }
}

你可能感兴趣的:(golangchannel)