go 信道chan有缓冲通道跟无缓冲通道区别:

go通道chan:
通道(channel)是用来传递数据的一个数据结构,通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯,操作符 <- 用于指定通道的方向,发送或接收,如果未指定方向,则为双向通道。
1、无缓冲:
要求发送方和接收方的goroutine同时准备好,才能完成发送和接收操作。
如果两个goroutine都没有准备好,通道会导致先执行发送或者接收的goroutine阻塞等待,这种对通道进行发送跟接收的交互行为本身就是同步的。

2:有缓冲的通道:
缓冲信道的容量:
是指信道可以存储的值的数量。我们在使用 make 函数创建缓冲信道的时候会指定容量大小。

缓冲信道的长度:是指信道中当前排队的元素个数。

一种在被接收前能存储一个或多个值的通道。并不强制goroutine直接必须同时完成发送和接收,只有通道中没有要接收的值时,接收动作才会阻塞,只有在通道没有可用的缓冲区容纳被发送的值时,发送动作才会阻塞。

带缓冲通道在很多特性上和无缓冲通道是类似的。无缓冲通道可以看作是长度永远为 0 的带缓冲通道。因此根据这个特性,带缓冲通道在下面列举的情况下依然会发生阻塞:
1、带缓冲通道被填满时,尝试再次发送数据时发生阻塞。
2、带缓冲通道为空时,尝试接收数据时发生阻塞。

channel存在3种状态:

  1. nil,未初始化的状态,只进行了声明,或者手动赋值为nil ;
  2. active,正常的channel,可读或者可写 ;
  3. closed,已关闭,千万不要误认为关闭channel后,channel的值是nil,关闭的状态的chan仍然可以读值(取值),但不能写值(会报panic: send on closed channel),nil状态的chan是不能close(panic: close of nil channel)。
func main() {
	var a chan int
	fmt.Println(a) // <nil>
	a = make(chan int)
	close(a)
	fmt.Println(a) // 0xc00003e060
}

channel可进行3种操作:
1、读;
2、写;
3、关闭。
在这里插入图片描述
对于nil通道的情况,也并非完全遵循上表,有1个特殊场景:当nil的通道在select的某个case中时,这个case会阻塞,但不会造成死锁。

下面介绍使用channel的10种常用操作:

  1. 使用for range读channel

场景:当需要不断从channel读取数据时
原理:使用for-range读取channel,这样既安全又便利,**当channel关闭时,for循环会自动退出,**无需主动监测channel是否关闭,可以防止读取已经关闭的channel,造成读到数据为通道所存储的数据类型的零值。
注意:只适合有缓冲chan

var syn sync.WaitGroup
var sss int

func main() {
	sss = 2
	syn.Add(1)
	a := make(chan int, sss)
	a <- 4
	a <- 8
	go Aaa(a)
	syn.Wait()
}

func Aaa(a chan int) {
	defer syn.Done()
	for x := range a {
		sss--
		if sss == 0 {
			close(a)
		}
		fmt.Println("==", x)
	}
}
  1. 使用_,ok判断channel是否关闭

场景:读channel,但不确定channel是否关闭时
原理:读已关闭的channel会造成零值 ,如果不确定channel,需要使用ok进行检测。ok的结果和含义:
true:读到数据,并且通道没有关闭。
false通道关闭,且无数据读到。

注意:只适合有缓冲chan

var syn sync.WaitGroup
var sss int

func main() {
	sss = 2
	syn.Add(1)
	a := make(chan int, sss)
	a <- 405
	close(a)
	go Aaa(a)
	syn.Wait()
}

func Aaa(a chan int) {
	defer syn.Done()
	for {
		v, ok := <-a
		fmt.Println(v, ok)
		if !ok {
			break
		}
	}
}

输出:

405 true
0 false

3、读写

package main

import (
	"fmt"
	"time"
)

func WriteData(intChan chan int)  {
	for i:=1;i<=50;i++{
		intChan<-i
		fmt.Println("写:",i)
		time.Sleep(time.Millisecond*100)
	}
	close(intChan) //写完后关闭管道
}

func ReadData(intChan chan int,exitChan chan bool)  {
	for{
		v,ok:=<-intChan
		if !ok{
			fmt.Println("读取完毕")
			break
		}
		fmt.Println("读:",v)
	}

	//设置全局标志 告诉main 读取完毕了 main主线程可以关闭了
	exitChan<-true
	close(exitChan)
}

func main() {

	intChan :=make(chan int,50)
	exitChan:=make(chan bool,1)

	go WriteData(intChan)
	go ReadData(intChan,exitChan)

	//阻塞主线程
	for ok:=range exitChan{
		fmt.Println(ok)
	}
}

4、管道实现互斥锁

var counter = 0

func Increase1000(id int, done chan bool, mutex chan bool) {
	for i := 0; i < 1000; i++ {
		mutex <- true //加锁
		counter += 1
		time.Sleep(time.Microsecond)
		<-mutex //解锁
	}
	done <- true
}

func main() {
	mutex, done := make(chan bool, 1), make(chan bool)
	go Increase1000(1, done, mutex)
	go Increase1000(2, done, mutex)

	<-done
	<-done
	log.Println(counter)
}

5、管道实现定时通知
注意:有缓冲和无缓冲通道都可以实现

func Notice(d time.Duration, c chan bool) chan bool {
	time.Sleep(d) //定时
	c <- true
	return c
}

func main() {
	c := make(chan bool)
	log.Println("one")
	go Notice(time.Second, c)
	<-c //管道没有写则阻塞
	log.Println("tow")
	go Notice(time.Second, c)
	<-c
	log.Println("three")
}
func Notice(d time.Duration) chan bool {
	c := make(chan bool, 1)
	time.Sleep(d) //定时
	c <- true
	close(c)
	return c
}

func main() {
	log.Println("one")
	<-Notice(time.Second) //管道没有写则阻塞
	log.Println("tow")
	<-Notice(time.Second)
	log.Println("three")
}

6、通过两个 goroutine 来计算数字之和

package main

import "fmt"

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // 把 sum 发送到通道 c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0, 8, 6, 78}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // 从通道 c 中接收

	fmt.Println(x, y, x+y)
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心码农1号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值