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() //手动停止定时器
posted @ 2025-10-18 22:58  九九4251  阅读(11)  评论(0)    收藏  举报