管道(channel)基本介绍
(1)channel本质就是一个数据结构——队列
(2)数据先进先出
(3)线程安全,多goroutine访问时,不需要加锁,channel本身就是线程安全的
(4)channel是有类型的,一个int类型的channel只能存放int类型的数据
(5)定义声明chan:
var chan_name chan 变量类型
- channel是引用类型
- channel必须初始化才能写入数据,即make后才能使用
- 一种类型的channel只能写入一种类型的数据
channel实例
简单channel示例
package main import ( "fmt" ) func main() { // 创建一个可以存放3个int类型的管道 var intChan chan int intChan = make(chan int, 3) // 打印intChan fmt.Printf("intChan的值 = %v intChan本身的地址 = %p\n", intChan, &intChan) // 向管道写入数据,当我们向管道写入数据时,不可以超过其容量 intChan <- 10 num := 100 intChan <- num // 管道的长度和cap(容量) fmt.Printf("intChan len = %v cap=%v\n", len(intChan), cap(intChan)) // 从管道中读取数据 var num2 int num2 = <- intChan fmt.Println("num2 =", num2) // 管道的长度和cap(容量) fmt.Printf("intChan len = %v cap=%v\n", len(intChan), cap(intChan)) // 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告deadlock }
- channel注意事项:
- channel只能存放指定数据类型
- channel的数据放满了就不能再放入了
- 如果从channel中取出数据就可以继续放入
- 在没有使用协程的情况下,如果channel的数据取完了,再取就会报deadlock
map chan示例
package main import ( _"fmt" ) func main() { var mapChan chan map[string]string mapChan = make(chan map[string]string, 10) m1 := make(map[string]string, 20) m1["city1"] = "New York" m1["city2"] = "Washington DC" m2 := make(map[string]string, 20) m2["name1"] = "Sophia" m2["name2"] = "Maria" mapChan <- m1 mapChan <- m2 }
结构体 chan示例
package main import ( "fmt" ) type Cat struct{ Name string Age int } func main() { var catChan chan Cat catChan = make(chan Cat, 10) cat1 := Cat{Name:"Tom", Age:3,} cat2 := Cat{Name:"Jack", Age:4,} catChan <- cat1 catChan <- cat2 cat11 := <- catChan cat22 := <- catChan fmt.Println(cat11, cat22) fmt.Println(cat11.Name) }
结构体指针 chan 示例
package main import ( "fmt" ) type Cat struct{ Name string Age int } func main() { var catChan chan *Cat catChan = make(chan *Cat, 10) cat1 := Cat{Name:"Tom", Age:3,} cat2 := Cat{Name:"Jack", Age:4,} catChan <- &cat1 catChan <- &cat2 cat11 := <- catChan cat22 := <- catChan fmt.Println(cat11, cat22) fmt.Println(cat11.Name) }
interface chan示例
package main import ( "fmt" ) type Cat struct{ Name string Age int } func main() { // 定义一个存放任意数据类型的管道,3个数据 // var allChan chan interface{} // allChan = make(chan interface{}, 3) allChan := make(chan interface{}, 3) allChan <- 10 allChan <- "tom" cat := Cat{"blossom", 4} allChan <- cat // 获取管道中第三个元素 <- allChan <- allChan newCat := <-allChan fmt.Printf("newCat = %T, newCat = %v\n", newCat, newCat) // ./go_t.go:31:41: newCat.Name undefined (type interface {} is interface with no methods) // fmt.Printf("newCat.Name = %v\n", newCat.Name) // 正确方法,使用类型断言 a := newCat.(Cat) fmt.Printf("newCat.Name = %v\n", a.Name) }
channel的关闭和遍历
(1)channel的关闭
- 使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据
(2)channel的遍历
- channel支持for-range的方式进行遍历,但是请注意以下细节:
- 在遍历时,如果channel没有关闭,则会出现deadlock的错误
- 在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
(3)channel的关闭和遍历示例:
package main import ( "fmt" ) func main() { intChan := make(chan int, 3) intChan <- 100 intChan <- 200 // close close(intChan) fmt.Println("ok") // 管道关闭后是可以读取数据的 n1 :=<- intChan fmt.Println("n1 =", n1) // 遍历 intChan2 := make(chan int, 100) for i := 0; i < 100; i++ { // 放入100个数据到intChan2 intChan2 <- i * 2 } // 这种遍历方式只能取出50个数字,因为每取出一个数字,len(intChan2)就减一 // for i := 0; i < len(intChan2); i++ { // } close(intChan2) // for-range取值没有下标不需要 _, v := range intChan2 for v := range intChan2 { // 如果管道没有关闭,那么数据是可以全部取出来的,但是会有错误:fatal error: all goroutines are asleep - deadlock! fmt.Println("v =", v) } }
参考资料大全
Go资料大全:https://github.com/0voice/Introduction-to-Golang
Go中文网链接:https://studygolang.com/
Golang标准文档:https://studygolang.com/pkgdoc