基本介绍
- channel本质就是一个数据结构——队列
- 数据是先进先出
- 线程安全,多 goroutine 访问时,不需要加锁
- channel是有类型的(string,int,interface 类型断言)
定义声明channel
var 变量名 chan 数据类型
var intChan chan int // intChan 用于存放 int 数据
var mapChan chan map[int]string // mapChan 用于存放 map[int]string 类型数据
var perChan chan Person // 结构体
var perChan chan *Person // 结构体指针
- channel 是引用类型
- channel 必须初始化才能写入数据,即 make 后才能使用
- channel 是有类型的, (比如: intChan 只能写入 int 类型数据)
- channel 中的数据取完之后,再继续取就会报死锁: fatal error: all goroutines are asleep - deadlock!
代码
package main
import "fmt"
func main() {
// 1、声明 chan 类型的数据,存放 3 个int
var intChan chan int
fmt.Println("intChan =", intChan) // nil
// 2、初始化
intChan = make(chan int, 3)
// intChan类型: chan int, 值: 0xc00001c100 , 本身的地址: 0xc000006028
fmt.Printf("intChan类型: %T, 值: %v , 本身的地址: %v\n", intChan, intChan, &intChan)
// 3、向管道(channel)写入数据
// 注意: 写入数据时,不能超过其容量,fatal error: all goroutines are asleep - deadlock!
// 致命错误:所有goroutine都处于休眠状态-死锁!
intChan <- 10
num := 211
intChan <- num
intChan <- 12
// 4、查看channel的长度和容量
fmt.Printf("intChan长度: %v, 容量: %v \n", len(intChan), cap(intChan))
// 5、读取 channel - 队列(先进先出FIFO)
var num2 int = <-intChan
fmt.Println("num2 = ", num2)
fmt.Printf("intChan长度: %v, 容量: %v \n", len(intChan), cap(intChan))
// 6、在没有使用协程的情况下,如果我们的管道数据已经全部取出,再继续取就会报 deadlock (死锁)
num3 := <-intChan
num4 := <-intChan
// num5 := <-intChan // channel 里已经没有数据,不能再取了 fatal error: all goroutines are asleep - deadlock!
fmt.Println("num3 = ", num3, "num4 = ", num4)
}
interface{} 类型的 channel
任何数据类型都实现了空接口,使用的时候要先断言数据类型
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
var allChan chan interface{} = make(chan interface{}, 10)
allChan <- 10
cat1 := Cat{Name: "tom", Age: 2}
cat2 := Cat{Name: "tom02", Age: 3}
allChan <- cat1
allChan <- cat2
allChan <- "jack"
// 取出
item := <-allChan
fmt.Printf("item类型:%T, 值: %v \n", item, item)
// item类型:int, 值: 10
// 类型断言
newItem, ok := item.(Cat)
fmt.Printf("newItem 类型:%T, 值: %v, ok = %v", newItem, newItem, ok)
// newItem 类型:main.Cat, 值: { 0}, ok = false
}
channel 的关闭和遍历
channel的关闭
使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据。
channel的遍历
channel不能使用常规for循环遍历,因为每取一次数据,channel的长度就减1
channel支持for—range的方式进行遍历,请注意两个细节
1、在遍历时,如果channel没有关闭,则回出现 deadlock 的错误
2、在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。
代码
// channel 的关闭和遍历
package main
import "fmt"
func main() {
// 1、关闭
intChan := make(chan int, 3)
intChan <- 100
intChan <- 200
close(intChan)
// intChan <- 300
// 关闭之后不能写入 panic: send on closed channel
fmt.Println("intChan = ", intChan) // intChan = 0xc000118080
// 关闭之后可以读取
n1 := <-intChan
fmt.Println("n1 = ", n1) // n1 = 100
// 2、遍历
intChan2 := make(chan int, 100)
for i := 1; i <= 100; i++ {
intChan2 <- i // 放入100个数据
}
// 常规for循环只能遍历取出50个数据 - 因为每取一个,channel 的长度就 -1
/* for i := 1; i <= len(intChan2); i++ {
fmt.Printf("第%v个值:%v \n", i, <-intChan2)
} */
// for range 遍历, 没有 index
close(intChan2)
// 如果遍历时,没有关闭channel, 就会出现死锁 deadlock; 关闭后,则会正常遍历,遍历完之后,退出程序
for v := range intChan2 {
fmt.Println("v=", v)
}
}