Golang学习笔记--Channel

483 阅读3分钟

如何定义使用

定义

每个通道都有与之关联的类型。此类型是允许通道传输的数据类型。不允许使用该通道传输其他类型的数据。

通道的零值为nil。零通道没有任何用处,因此必须使用类似于 map 和 slice 的make来定义。

package main

import "fmt"

func main() {  
    var a chan int      // 定义 channel
    if a == nil {
        fmt.Println("channel a is nil, going to define it")
        a = make(chan int)
        fmt.Printf("Type of a is %T", a)   // 输出Type of a is chan int  
    }
}

使用(接收和发送消息)

data := <- a // read from channel a  
a <- data // write to channel a  

默认情况下,发送和接收到通道处于阻塞状态。当数据发送到通道时,主goroutine将在send语句中被阻塞,直到其他Goroutine从该通道读取数据为止。同样,当从通道读取数据时,将阻止读取,直到某些Goroutine将数据写入该通道为止。

此属性可帮助Goroutines有效通信,而无需使用其他编程语言中很常见的显式锁或条件变量。

一个简单的程序:程序实现 (1 * 1) + (2 * 2) + (3 * 3) + ... (n * n) + (1 * 1 * 1) + (2 * 2 * 2) + (3 * 3 * 3) + ...(n * n * n)可以拆成两部分并发执行,一个 goroutine 计算平方和,一个计算立方和

package main

import (  
    "fmt"
)

func calcSquares(number int, squareop chan int) {  
    sum := 0
    for number != 0 {
        digit := number % 10
        sum += digit * digit
        number /= 10
    }
    squareop <- sum
}

func calcCubes(number int, cubeop chan int) {  
    sum := 0 
    for number != 0 {
        digit := number % 10
        sum += digit * digit * digit
        number /= 10
    }
    cubeop <- sum
} 

func main() {  
    number := 10
    sqrch := make(chan int)
    cubech := make(chan int)
    go calcSquares(number, sqrch)
    go calcCubes(number, cubech)
    squares, cubes := <-sqrch, <-cubech  // 利用 channel 使主 goroutine 等待结果    
    fmt.Println("Final output", squares + cubes)
}

死锁

有一个地方需要注意,在使用 channel 时可能会引发死锁,看下面一个例子

func main() {  
    ch := make(chan int)
    ch <- 5             
}

这段程序向 channel 发送数字5,但是没有接收方,所以会一直阻塞

单向通道

上述简单的例子展示了双向 channel 如何使用,但是也可以创建单向通道,即仅发送或接收数据的通道。

// 定义只发送channel
sendch := make(chan<- int)

但是这样做又有什么意义呢,无法从 channel 中读取数据。这是使用 channel 转换。可以将双向通道转换为仅发送或仅接收通道,反之亦然。

// 这里将 channel 转换为单项只发送
func sendData(sendch chan<- int) {  
    sendch <- 10
}

func main() {  
    // 定义一个双向 channel
    chnl := make(chan int)
    go sendData(chnl)
    fmt.Println(<-chnl)
}

关闭通道

发送者可以关闭该通道,以通知接收者该通道将不再发送任何数据。接收器可以在从通道接收数据时使用附加变量,以检查通道是否已关闭。

v, ok := <- ch  

在上面的语句中,如果该值是通过对通道的成功发送操作接收到的,则ok为真。如果ok为假,则表示我们正在从封闭的通道读取数据。从关闭的通道读取的值将是通道类型的零值。例如,如果通道是int通道,则从封闭通道接收的值将为0。

channel 和 for range 可以搭配使用

func producer(chnl chan int) {  
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {  
    ch := make(chan int)
    go producer(ch)
    for v := range ch {       // 若channel 未关闭,则一直取数据
        fmt.Println("Received ",v)
    }
}

如果喜欢,请关注我的公众号,或者查看我的博客 http://packyzbq.coding.me. 我会不定时的发送我自己的学习记录,大家互相学习交流哈~weixin