Channel小结
一:channel的一些特性
1.尽量避免使用锁来解决临界资源安全问题
2.通道的角色必须在两个及以上
3.chan,必须要作用在两个以上的goroutine
二:通道的声明
点击查看代码
func main() {
//声明+赋值
var c chan int
c = make(chan int, 10)
/*//存,取
//存
c <- 1
//取
data := <-c
fmt.Println(data)*/
go func() {
time.Sleep(5*time.Second)
c <- 2
}()
// 另一个goroutine可以从通道中取数据
//阻塞等待c通道写入数据。如果没人写只有读,就会一直阻塞
data := <- c
fmt.Println("ch data;", data)
}
三:死锁产生的情况
1.单线程无法消费channel,写入一个数据至channel,却没人来消费这个数据,会导致阻塞
2.两个channel互相需要对方的数据,但是由于判断(select监听),拿不到对方的数据
3.sync锁产生的死锁
4.接收端等待,没有写入端
四:通道关闭
我们需要将通道手动关闭的目的,是为了告诉读取端,写入端的所有数据已写入完毕
我们通常也会将for range和close进行搭配使用,for range在close关闭后且已经读取完通道内的数据后,就会自动退出for循环
点击查看代码
import "fmt"
func main() {
chan1 := make(chan int) //通道可以当做参数传递
go test1(chan1)
//读取chan中的数据
/*for {
time.Sleep(1 * time.Second)
//判断chan状态是否关闭,如果关闭,就不会取值了
data1, ok := <-chan1
if !ok {
fmt.Println("读取完毕")
break
}
fmt.Println("ch data1", data1) //这种是原始写法
}*/
//读取chan中的数据,for 一个个取,并且会自动判断通道是否关闭
for v := range chan1{
time.Sleep(1*time.Second)
fmt.Println(v)
}
fmt.Println("end")
}
func test1(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
//关闭通道,告诉接收方,我不会再有其他数据发送到chan中了
close(ch)
}
五:缓冲通道
带了一个缓冲区的通道,只有当通道全部被写满后才会被阻塞
点击查看代码
package main
import (
"fmt"
"time"
)
func main() {
//缓冲通道
//缓冲区通道,放入数据,不会产生死锁,它不需要等待其他线程来拿,它可以放多个数据
//如果缓冲区满了,还没有人取,还会产生死锁
ch := make(chan int, 4)
fmt.Println(cap(ch), len(ch))
go test1(ch)
for v := range ch {
time.Sleep(1 * time.Second)
fmt.Println("main读取数据", v)
}
fmt.Println("main-end")
}
func test1(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
fmt.Println("放入数据:", i)
}
close(ch)
}
六:单向通道
在函数中,我们通常会要求这个通道只用于读数据,当我们操作失误,这里出现了写入,这就会造成混乱
我们通常将单向通道作为函数的形参传递,只能读或者只能写
点击查看代码
package main
import (
"fmt"
"time"
)
func main() {
ch1:=make(chan int,10)
ch1<-100
data:=<-ch1
fmt.Println(data)
//单向通道
ch2:=make(chan<-int,1) //只能写数据,不能读
ch2<-100
//data:=<-ch2 会报错
//ch3:=make(<-chan int,1) //只能读数据,不能写
//ch3<-100 会报错
//使用场景
ch4:=make(chan int) //可读可写
go writeOnly(ch4)
go readOnly(ch4)
time.Sleep(5*time.Second)
}
//指定函数去写,不让他读取,防止通道滥用
func writeOnly(ch chan <-int) {
//函数内部处理写数据的操作
ch <- 100
//尝试读操作
}
//指定函数去读,不让他写入,防止通道滥用
func readOnly(ch<-chan int) {
data:=<-ch
fmt.Println(data)
}
七:timer定时器
可以控制程序在某个时间点做某个事情
点击查看代码
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
//创建了一个timer对象
//C <-chan Time
//timer对象中的字段是一个channel,而且是一个读取的单向通道,我们将其赋值出来
//timeChan:=timer.C //这就是一个单向通道
fmt.Println(<-timer.C) //打印了定时器通道中存储的时间
//上面我们将里面的时间写入了通道,然后我们再通过读单向通道将其读取出来
}
//定时器(提前关闭)
//当我们创建一个定时器,会向Timer.C的通道中放入一个时间
//当我们在下面消费这个通道的时间后,这个就不会阻塞了,直接就结束了
timer2 := time.NewTimer(time.Second * 5)
go func() {
<-timer2.C //消费
fmt.Println("end")
}()
timer2.Stop() //手动停止定时器

浙公网安备 33010602011771号