Dawn's Blogs

分享技术 记录成长

0%

GO学习笔记 (5) 管道

管道channel基本介绍

channel的介绍

  • channel的本质就是一个队列,数据先进先出
  • 管道是线程安全的,多goroutine访问时,无需加锁

channel基本使用

1
2
3
4
5
6
7
8
// 声明
变量名 := make(chan 数据类型, cap)

// 向管道写入数据
管道变量 <- 数据

// 从管道中读取数据
变量 = <- 管道变量

注意:

  • channel是引用类型是指针,初始化后才能使用)
  • channel是有对应数据类型的

空接口实现存放任意类型的管道

由于任何类型都实现了空接口,即空接口变量可以被赋予任何类型的变量,所以可以利用用空接口实现可以存放任意类型的管道

1
2
3
4
5
6
7
8
// 利用空接口定义一个可以存放任何数据类型的管道
allChan := make(chan interface{}, 3)

allChan <- 10
allChan <- "smith"

person := Person{Name: "tom", Age: 18}
allChan <- person

注意:

从空接口类型的管道中取出数据时,注意类型断言

管道的关闭和遍历

管道的关闭

1
func close(c chan<- Type)

使用内置函数close可以关闭管道,当管道关闭时不能再向管道数据,但仍可以从管道中数据

对于已关闭并且没有数据的管道,语句:

1
x, ok := <-c

还会将ok置为false

管道的遍历

channel支持for-range遍历,注意

  • 如果管道没有关闭,则出现deadlock错误(不向管道中发送数据,会一直阻塞)
  • 如果管道已经关闭,可以结束遍历
1
2
3
4
// 管道的遍历
for val := range intChan {
// ...
}

无缓冲区和有缓冲区管道

有缓冲区管道

在有缓冲区的管道中,只要不超过缓冲区大小,不会出现写阻塞

1
2
3
4
5
6
7
// 创建有3个缓冲区大小的管道
intChan := make(chan int, 3)

// 连续写入三个数据,不会阻塞
intChan <- 1
intCHan <- 2
intCHan <- 3

无缓冲区的管道

在没有缓冲区的管道中,只要管道中的数据没有被拿走,始终会阻塞发送方

1
2
3
4
5
6
7
8
9
10
11
12
var intChan = make(chan int)
var str string

go func() {
str = "hello world"
<-intChan
}()

intChan <- 0
fmt.Println(str)

// 会输出:hello world

上述代码一定会输出hello world因为在发送方向管道写数据时,由于没有接收方管道会被直接阻塞。当有接收方想要从管道中读取数据时,发送方才会被唤醒。

只读/只写管道

在默认情况下,管道时双向的,可读可写

但是管道是可以声明为只读或者只写的。

只写管道

只写管道的数据类型为chan<- 管道数据类型

只读管道

只读管道的数据类型为<-chan 管道数据类型

应用场景

若有两个协程send和recv,send只负责发送数据,recv只负责接收数据。

1
2
3
4
5
// send只负责发送数据,故只向管道中写数据
// send使用只写管道
func send(ch chan<- string, msg string) {
// ...
}
1
2
3
4
5
// recv只负责接收数据,故只从管道中读取数据
// recv使用只读管道
func recv(ch <-chan string, msg string) {
// ...
}

select关键字

select可以解决从管道取数据的阻塞问题。类似于switch语句,每个case必须是一个通信(管道)操作,要么发送要么接收。

select 随机执行一个可运行的 case。如果没有case 可运行,它将阻塞,直到有case可运行。

如果有多个case都可以运行,select随机公平地选出一个执行。其他不会执行。

1
2
3
4
5
6
7
8
9
10
11
for {
select {
case v := <-intChan:
fmt.Printf("从intChan读取数据%d\n", v)
case v := <-stringChan:
fmt.Printf("从stringChan读取数据%v\n", v)
default:
fmt.Println("都取不到了")
return
}
}