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

go 信道chan有缓冲通道跟无缓冲通道区别:go通道chan:1、无缓冲:要求发送方和接收方的goroutine同时准备好,才能完成发送和接收操作。如果两个goroutine都没有准备好,通道会导致先执行发送或者接收的goroutine阻塞等待,这种对通道进行发送跟接收的交互行为本身就是同步的。2:有缓冲的通道:一种在被接收前能存储一个或多个值的通道。并不强制goroutine直接必须同时完成发送和接收,只有通道中没有要接收的值时,接收动作才会阻塞,只有在通道没有可用的缓冲区容纳被发送的值时,发送动作才会阻塞。…

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)
}

今天的文章go 信道chan有缓冲通道跟无缓冲通道区别:分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/31329.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注