基本介绍
- 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 mainimport "fmt"func main() {// 1、声明 chan 类型的数据,存放 3 个intvar intChan chan intfmt.Println("intChan =", intChan) // nil// 2、初始化intChan = make(chan int, 3)// intChan类型: chan int, 值: 0xc00001c100 , 本身的地址: 0xc000006028fmt.Printf("intChan类型: %T, 值: %v , 本身的地址: %v\n", intChan, intChan, &intChan)// 3、向管道(channel)写入数据// 注意: 写入数据时,不能超过其容量,fatal error: all goroutines are asleep - deadlock!// 致命错误:所有goroutine都处于休眠状态-死锁!intChan <- 10num := 211intChan <- numintChan <- 12// 4、查看channel的长度和容量fmt.Printf("intChan长度: %v, 容量: %v \n", len(intChan), cap(intChan))// 5、读取 channel - 队列(先进先出FIFO)var num2 int = <-intChanfmt.Println("num2 = ", num2)fmt.Printf("intChan长度: %v, 容量: %v \n", len(intChan), cap(intChan))// 6、在没有使用协程的情况下,如果我们的管道数据已经全部取出,再继续取就会报 deadlock (死锁)num3 := <-intChannum4 := <-intChan// num5 := <-intChan // channel 里已经没有数据,不能再取了 fatal error: all goroutines are asleep - deadlock!fmt.Println("num3 = ", num3, "num4 = ", num4)}
interface{} 类型的 channel
任何数据类型都实现了空接口,使用的时候要先断言数据类型
package mainimport "fmt"type Cat struct {Name stringAge int}func main() {var allChan chan interface{} = make(chan interface{}, 10)allChan <- 10cat1 := Cat{Name: "tom", Age: 2}cat2 := Cat{Name: "tom02", Age: 3}allChan <- cat1allChan <- cat2allChan <- "jack"// 取出item := <-allChanfmt.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 mainimport "fmt"func main() {// 1、关闭intChan := make(chan int, 3)intChan <- 100intChan <- 200close(intChan)// intChan <- 300// 关闭之后不能写入 panic: send on closed channelfmt.Println("intChan = ", intChan) // intChan = 0xc000118080// 关闭之后可以读取n1 := <-intChanfmt.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 遍历, 没有 indexclose(intChan2)// 如果遍历时,没有关闭channel, 就会出现死锁 deadlock; 关闭后,则会正常遍历,遍历完之后,退出程序for v := range intChan2 {fmt.Println("v=", v)}}
