32

Golang学习笔记--Channel

 4 years ago
source link: https://studygolang.com/articles/24860
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

如何定义使用

定义

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

通道的零值为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 . 我会不定时的发送我自己的学习记录,大家互相学习交流哈~

En6fA3F.jpg!web

weixin


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK