golang之管道
一、基本介绍
- channle本质就是一个数据结构-队列
- 数据是先进先出
- 线程安全,多个goroutine访问时,不需要加锁,就是说channel本身就是线程安全的
- channel是有类型的,一个string的channel只能存放string类型数据
二、定义/声明channel
var 变量名 chan 数据类型
示例:
var intChan chan int // (intChan用于存放int数据)
var mapChan chan map[string]string // (mapChan用于存放map[string]string类型)
var perChan chan Person // (perChan用于存放Person类型)
var perChan2 chan *Person // (perChan2用于存放Person的指针类型)
说明:
- channel是引用类型
- channel必须初始化才能写入数据,即make后才能使用
- 管道是有类型的,intChan只能存放整数int
三、channel(管道)-基本使用
- channel初始化
package main
import "fmt"
func main() {
var intChan chan int
intChan = make(chan int, 3)
fmt.Printf("intChan的值=%v\n", intChan) // intChan的值=0xc00007a080
}
说明:可以看出管道是引用类型>说明:可以看出管道是引用类型
- 向channel中写入数据和读取数据
package main
import "fmt"
func main() {
var intChan chan int
intChan = make(chan int, 3)
// 向管道中写入数据
intChan <- 100
num := 200
intChan <- num
intChan <- 300
// intChan <- 10 // 注意点:当我们给管道写入数据时,不能超过其容量.否则会报all goroutines are asleep - deadlock!
// 看看管道的长度和容量
fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan)) // channel len=3 cap=3
// 从管道中读取数据
num1 := <-intChan
num2 := <-intChan
num3 := <-intChan
fmt.Printf("num1=%v,num2=%v,num3=%v\n", num1, num2, num3) // num1=100,num2=200,num3=300
// 在没有使用协程的情况下,如果我们管道的数据已经全部取出,再取就会报错
// num4 := <-intChan // 报错all goroutines are asleep - deadlock!
}
说明:
- 当我们给管道写入数据时,不能超过其容量.否则会报all goroutines are asleep - deadlock!
- 在没有使用协程的情况下,如果我们管道的数据已经全部取出,再取就会报错
四、注意事项
- channel只能存放指定数据类型
- channel的数据类型放满后,就不能放入了
- 如果从channel取出数据后,可以继续放入
- 在没有使用协程的情况下,如果如果channel数据取完了,再取,就会报dead lock
五、channel的关闭
使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是任然可以从channel读取数据
package main
import "fmt"
func main() {
var intChan chan int
intChan = make(chan int, 3)
// 向管道中写入数据
intChan <- 100
num := 200
intChan <- num
// 关闭intChan管道
close(intChan)
// 当channel关闭后,就不能再向channel写数据了
// intChan <- 200 // 报错:panic: send on closed channel
fmt.Println("intChan=", intChan) // intChan= 0xc00007a080
num1 := <-intChan
// 当channel关闭后,任然可以从channel读取数据
fmt.Println("num1=", num1) // num1= 100
}
六、channel的遍历
channel支持for-range的方式遍历,注意以下两个细节:
- 在遍历时,如果channel没有关闭,则会出现deadlock的错误
- 在遍历时,如果channel已经关闭,则会正确遍历数据,遍历完后,就会退出遍历
package main
import "fmt"
func main() {
var intChan chan int
intChan = make(chan int, 3)
// 向管道中写入数据
intChan <- 100
num := 200
intChan <- num
intChan <- 300
// 在遍历时,如果channel没有关闭,则会出现deadlock的错误
// 在遍历时,如果channel已经关闭,则会正确遍历数据,遍历完后,就会退出遍历
close(intChan)
for v := range intChan {
fmt.Println("v=", v)
}
}
输出结果:
v= 100
v= 200
v= 300