#channel 介绍

channel 提供了一种通信机制,通过它,一个 goroutine 可以想另一 goroutine 发送消息。channel 本身还需关联了一个类型,也就是 channel 可以发送数据的类型。例如: 发送 int 类型消息的 channel 写作 chan int 。

#channel 创建

channel 使用内置的 make 函数创建,下面声明了一个 chan int 类型的 channel:

ch := make(chan int)

c和 map 类似,make 创建了一个底层数据结构的引用,当赋值或参数传递时,只是拷贝了一个 channel 引用,指向相同的 channel 对象。和其他引用类型一样,channel 的空值为 nil 。使用 == 可以对类型相同的 channel 进行比较,只有指向相同对象或同为 nil 时,才返回 true

#channel 的读写操作

ch := make(chan int)

// write to channel
ch <- 123

// read from channel
x := <- ch

// another way to read
x = <- chnnel 一定要初始化后才能进行读写操作,否则会永久阻塞。

channel 一定要初始化后才能进行读写操作,否则会永久阻塞。

#关闭 channel

golang 提供了内置的 close 函数对 channel 进行关闭操作。

ch := make(chan int)
close(ch)

有关 channel 的关闭,你需要注意以下事项:


  1. 关闭一个未初始化(nil) 的 channel 会产生 panic
  2. 重复关闭同一个 channel 会产生 panic
  3. 向一个已关闭的 channel 中发送消息会产生 panic
  4. 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel中还未被读取的消息,若消息均已读出,则会读到类型的零值。从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为
  5. false 的 ok-idiom,可以用它来判断 channel 是否关闭
  6. 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息

ch := make(chan int, 10)
ch <- 11
ch <- 12

close(ch)

for x := range ch {
fmt.Println(x)
}

x, ok := <- ch
fmt.Println(x, ok)


-----
output:

11
12
0 false

#channel 的类型

channel 分为不带缓存的 channel 和带缓存的 channel。

#无缓存的 channel

从无缓存的 channel 中读取消息会阻塞,直到有 goroutine 向该 channel 中发送消息;同理,向无缓存的 channel 中发送消息也会阻塞,直到有 goroutine 从 channel 中读取消息。

通过无缓存的 channel 进行通信时,接收者收到数据 happens before 发送者 goroutine 唤醒

#有缓存的 channel

有缓存的 channel 的声明方式为指定 make 函数的第二个参数,该参数为 channel 缓存的容量

ch := make(chan int, 10)

有缓存的 channel 类似一个阻塞队列(采用环形数组实现)。当缓存未满时,向 channel 中发送消息时不会阻塞,当缓存满时,发送操作将被阻塞,直到有其他 goroutine 从中读取消息;相应的,当 channel 中消息不为空时,读取消息不会出现阻塞,当 channel 为空时,读取操作会造成阻塞,直到有 goroutine 向 channel 中写入消息。

ch := make(chan int, 3)

// blocked, read from empty buffered channel
<- ch

上面的例子,没人写,读取一直被阻塞

ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3

// blocked, send to full buffered channel
ch <- 4

上面的例子,写入3个数据,耗尽缓存能力,再写就阻塞了

通过 len 函数可以获得 chan 中的元素个数,通过 cap 函数可以得到 channel 的缓存长度。

#实例

###通过channel实现同步

####导入依赖

import (
"fmt"
"time"
)
//语法点①:创建int类型的无缓存管道
//var ch = make(chan int)
var ch = make(chan int,0)

func Printer(str string) {
for _, data := range str {
fmt.Printf("%c", data)
time.Sleep(time.Second)
}
fmt.Printf("\n")
}

func person1() {
//打印完需要7秒钟
//劳资不打印完是不会往管道中塞数据的,阻塞不死你丫的
Printer("今生注定我爱你")

//箭头指向管道内部,写数据
//在打完今生注定我爱你(耗时7秒钟)后,才写入数据
//语法点②:向管道里写数据,无论读写,箭头只能朝左
//语法点⑤:如果管道缓存已满,则阻塞等待至有人取出数据腾出空间,再写入
ch <- 666
}

func person2() {
//箭头指向管道外面,代表从管道中拿出数据,读数据

//语法点③:从管理取出数据,但不不接收
//语法点⑥:管道里没数据时,阻塞死等
<-ch

//语法点④:从管理取出数据,且使用data变量接收
//data:=<-ch
//fmt.Println("读出数据:",data)

//终于妈的可以打印了
Printer("FUCKOFF")
}

func main() {

go person1()
go person2()

//主协程赖着不死
for {
time.Sleep(time.Second)
}
}

###通过channel实现同步和数据交互

package main

import (
"fmt"
"time"
)

func main() {
//创建无缓存管道
ch := make(chan string)

//5、主协程结束
defer fmt.Println("主协程也结束")

//子协程负责写数据
go func() {
//3、结束任务
defer fmt.Println("子协程调用完毕")

//1、缓缓打印2次序号
for i := 0; i < 2; i++ {
fmt.Println("子协程 i= ", i)
time.Sleep(time.Second)
}

//2、向管道发送数据
ch <- "我是子协程,工作完毕"
}()

//4、阻塞接收
str := <-ch
fmt.Println("str = ", str)
}

###无缓冲的channel

package main

import (
"fmt"
"time"
)

func main() {
//创建一个无缓冲的管道
ch := make(chan int, 1)

//长度0,缓存能力0
fmt.Printf("len(ch) = %d, cap(ch)=%d\n", len(ch), cap(ch))

go func() {
//向管道中存入0,被阻塞,存入1,被阻塞,存入2
for i := 0; i < 3; i++ {
fmt.Println("子协程: i = ", i)
ch <- i

fmt.Println("5秒以内被打印出来给杰神100万!")
}
}()

//睡眠2秒
time.Sleep(5 * time.Second)

//读取0,被阻塞,读取1,被阻塞,读取2
for i := 0; i < 3; i++ {
num := <-ch
fmt.Println("num = ", num)
}

}

###有缓存的channel

package main

import (
"fmt"
"time"
)

func main() {
//创建3缓存的管道
ch := make(chan int, 3)
//长度0,缓存能力3(即使没人读,也能写入3个值)
fmt.Printf("len(ch) = %d, cap(ch) = %d\n", len(ch), cap(ch))

//一次性存入3个:012,3456789
go func() {
for i := 0; i < 10; i++ {
ch <- i
fmt.Printf("子协程存入[%d]: len(ch) = %d, cap(ch) = %d\n", i, len(ch), cap(ch))
//time.Sleep(1 * time.Second)
}
}()

//time.Sleep(5 * time.Second)

//一次性读取3个:012,345,678,9
for i := 0; i < 10; i++ {
num := <-ch
fmt.Println("num = ", num)
}
time.Sleep(1*time.Nanosecond)
}

学院Go语言视频主页

[清华团队带你实战区块链开发]


扫码获取海量视频及源码 QQ群:721929980