Golang channle (管道)基本介绍、快速入门
为什么需要channel?前面使用全局变量加锁同步来解决goroutine的通讯,但不完美1)主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。2)如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁3)通过全局变量加锁同步来实现通讯,也并不利用多个协程对全局变量的读写操作。4)上面种
channel(管道)-基本介绍
为什么需要channel?前面使用全局变量加锁同步来解决goroutine的通讯,但不完美
(1)主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。
(2)如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁。
(3)通过全局变量加锁同步来实现通讯,也并不利用多个协程对全局变量的读写操作。
(4)上面种种分析都在呼唤一个新的通讯机制channel
import (
"fmt"
"sync"
"time"
)
var (
m = make(map[int]int, 10)
//声明一个全局的互斥锁 lock是一个全局的互斥锁
//sync是包 同步的意思 mutex是互斥的意思
lock sync.Mutex
)
// test函数就是计算n的阶乘
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res = res * i
}
//将计算结果放到map当中 加锁
lock.Lock()
m[n] = res
lock.Unlock()
}
func main() {
//这里开启多协程完成任务
for i := 1; i <= 20; i++ {
go test(i)
}
time.Sleep(time.Second * 10)
for k, v := range m {
fmt.Println(k, v)
}
}
channel的介绍
(1)channle本质就是一个数据结构-队列,数据是先进先出
(2)线程安全,多goroutine访问时,不需要加锁,就是说channel本身就是线程安全的(管道是线程安全的,你在对管道读取的时候不管有多少个协程在对同一个管道操作,可以放心使用,不会出现错误,这些是有编译器在底层维护的)
(3)channel是有类型的,一个string的channel只能存放string类型数据。(如果管道想放int或者float那么可以使用空接口interface类型)
定义/声明channel nil类型
var 变量名 chan 数据类型,举例:
var intChan chan int (intChan用于存放int数据)
var mapChan chan map[int]string (mapChan用于存放map[int]string类型)
var perChan chan Person
var perChan2 chan *Person
说明:
(1)channel是引用类型
(2)channel必须初始化才能写入数据,即make后才能使用
(3)管道是有类型的,intChan只能写入整数int
channel初始化
说明:使用make进行初始化
var intChan chan int
intChan =make(chan int,10)
向channel中写入(存放)数据
var intChan chan int
intChan = make(chan int,10)
num = 999
intChan <- 10
intChan <- num
如果将channel传给另外一个函数,那么在这个函数里面操作的是同一个管道,因为它是引用类型。
package main
import "fmt"
func main() {
var intChan chan int
//创建可以存放3个int类型的管道
intChan = make(chan int, 3)
//看看intChan是什么
fmt.Printf("initChan的值为=%v\n initChan本身地址为%p\n", intChan, &intChan)
//向管道写入数据
intChan <- 1
num := 2
intChan <- num
//当给管道写入数据的时候,不能超过其容量
//看看管道的长度和cap
fmt.Println("长度:", len(intChan), "容量:", cap(intChan))
num1 := <-intChan
fmt.Println("取出来的第一个数据是:", num1)
fmt.Println("取出之后的长度:", len(intChan), "取出之后的容量:", cap(intChan))
//在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告deadlock
}
initChan的值为=0xc00007a080
initChan本身地址为0xc00000a028
长度: 2 容量: 3
取出来的第一个数据是: 1
取出之后的长度: 1 取出之后的容量: 3
要将什么样的数据放到管道里面呢?就是这种数据是流动的,它一边往里面放,另外一边去读取,这样通信机制就形成了。<-ch这种接受方式也可以,将数据丢掉,数据不想要,这也是取的一种方式,只不过不想要。
channel 使用注意事项
(1)channel中只能存放指定的数据类型
(2)channle的数据放满后,就不能再放入了(会出现死锁的错误),如果从channel取出数据后,可以继续放入
(3)在没有使用协程的情况下,如果channel数据取完了,再取,就会报dead lock
读写channel案例
管道里面可以存放很多map,每个map里面又可以有多对的key/value。这里在使用map之前要先make一下 。
管道也是可以存放结构体实例的。
管道里面也可以存放指针。
如果希望在这个管道里面存放的是混合类型的数据,我们既想放结构体又想放字符串。这里的数据类型为空接口类型interface{},因为任何的数据类型都认为实现了空接口。
注意:取出来的时候记得使用类型断言。
package main
import (
"fmt"
"reflect"
)
type Cat struct {
Name string
Age int
}
func main() {
allChan := make(chan interface{}, 3)
cat1 := Cat{
Name: "1",
Age: 10,
}
allChan <- "hello"
allChan <- 1
allChan <- cat1
<-allChan
<-allChan
newCat := <-allChan
fmt.Println(reflect.TypeOf(newCat))
fmt.Printf("%T\n %#v\n", newCat, newCat)
//fmt.Println(c.Name, c.Age) 这种写法是错误的,编译器通不过,使用类型断言即可
c := newCat.(Cat)
fmt.Println(c.Name, c.Age)
}
6)存放函数
ch := make(chan func(string) string, 10)
ch <- func(name string) string { //使用匿名函数
return "hello " + name
}
更多推荐
所有评论(0)