Go-channel

管道(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

Go-channel

上一篇:回调函数


下一篇:地图下钻+标记点