【go基础】go并发编程之channel

目录

1.简介

2.channel类型

无缓冲区的channel

无缓冲区channel的创建

带缓冲区的channel

 带缓冲区channel的创建

3.channel使用代码演示

 4.获取channel中的值

​编辑

5.单向channel

单向发送data,发送到channel中

单向接收,channel接收数据

6.channel使用中的死锁


1.简介

channel通道是一种比较重要的数据结构,用于多个协程之间的通信。

可以把channel想象成一个传送带,将协程想象成传送带周边的人,一个人往传送带上放东西,传送带另一端的人将传送带的东西取走。

也就是左边的goroutine是负责发送数据的,右边的goroutine是负责接收数据的。data会通过channel,从左边的goroutine传送的右边的goroutine。

【go基础】go并发编程之channel_第1张图片

2.channel类型

 channel类型分为两种类型,一种是无缓冲的channel,一种是有缓冲的channel。

无缓冲区的channel

对于无缓冲区的channel来说,channel本身是不存储数据的,整个channel的发送和接收都会被阻塞。

如下图,左边协程发送数据到右边协程,当data还没有被右边的协程接收处理完的时候,左边的协程会被阻塞发送数据,左边的协程发送的data会被阻塞到外面,只有当右边的协程获取数据之后,左边的协程才能继续将数据发送出去。

【go基础】go并发编程之channel_第2张图片

无缓冲区channel的创建

①先声明再实例化

var c chan interface{}
c = make(chan interface{})

②直接实例化

c := make(chan interface{})

带缓冲区的channel

带缓冲区的channel会有暂存数据的功能,当channel缓冲区满了或者空(空的话可以理解为是不带缓冲区的channel)的时候,channel的发送和接收会被阻塞。

如下图:

channel为空的话,相当于是无缓冲区的channel。

假设定义一个缓冲区大小为3的channel,向里面发送三个data,当达到缓冲区的大小的时候,发送端的协程的数据会被阻塞,直到接收端的协程接收玩一个data数据,发送端的协程才可以发送下一个数据。

【go基础】go并发编程之channel_第3张图片

 带缓冲区channel的创建

与无缓冲区channel不同的是,带缓冲区的channel需要指定缓冲区大小。

①先声明,后初始化,创建一个缓冲区大小为2的channel

var c chan interface{}
c = make(chan interface{},2)

②直接创建

c := make(chan interface{},2)

3.channel使用代码演示

func main() {
    // 定义一个无缓冲的channel
	ch := make(chan int)
    
    // 开启一个协程,往里面添加1
	go func() {
		ch <- 1
	}()
    
    // 主协程获取channel里面的data
	v := <-ch
	fmt.Printf("v is %d",v)
}

那么无缓冲的协程阻塞怎么做到的呢?

首先创建一个无缓冲的通道 ,发送端发送一个,接收端接收一个值。

go协程和主协程是并发运行的,使用go关键字开启一个协程是需要时间初始化的,所以先执行的是

func main() {
	ch := make(chan string)
	s := []string{"A","B","C","D"}

	// 协程的创建需要一些时间
	go func() {
		// 执行玩,关闭chan,要不然主线程会一直从chan中获取空数据,导致panic
		defer close(ch)
		for _, v := range s {
			fmt.Printf("send to chan %v\n",v)
			ch <- v
			// 每间隔1秒往chan中发送一条数据
			time.Sleep(1 * time.Second)
		}
	}()
	fmt.Println("5秒开始")
	time.Sleep(5*time.Second)
	fmt.Println("5秒结束")
	
	// 获取chan中的数据
	for val := range ch {
		fmt.Printf("received %v\n",val)
	}
}

执行的结果:

首先执行主线程,5s开始,间隔5s,此时go线程初始化好,执行go协程中的内容,发送数据到通道,每间隔1s往chan中发送一条数据。此时主协程接收chan中的数据。

【go基础】go并发编程之channel_第4张图片

 那么有缓冲区的通道呢?

定义一个大小为2的有缓冲区的通道,当无缓冲区的时候,可以传送一个,加上缓冲区的大小

func main() {
	ch := make(chan string,2)
	s := []string{"A","B","C","D"}
	go func() {
		defer close(ch)
		for _, v := range s {
			fmt.Printf("send to chan %v\n",v)
			ch <- v
			// 每间隔1s发送一个数据
			time.Sleep(1*time.Second)
		}
	}()
	fmt.Println("5s开始")
	time.Sleep(5*time.Second)
	fmt.Println("5s结束")
	for c := range ch {
		fmt.Printf("received %v\n",c)
	}


}

【go基础】go并发编程之channel_第5张图片

 4.获取channel中的值

方式一:
<-ch

方式二:
for range
func main() {
	ch := make(chan int)
	go func() {
  // 		defer close(ch)  // 此时ok是false
      ch <-1

	}()

    // 获取管道中的值,一个是从管道中获取发送方发送的值,另一个是go中默认的一个值
    // ok的意思是成功从管道中获取值,ok就是true,否则就为false

	v, ok := <-ch
	fmt.Printf("%v is %v\n",v,ok)
}

【go基础】go并发编程之channel_第6张图片

5.单向channel

发送还是接收,具体看通道变量c 后面的变量

单向发送data,发送到channel中

var c chan<- interface{}
c = make(chan<- interface{})

c := make(chan<- interface{})

单向接收,channel接收数据

var c <-chan interface{}
c = make(<- chan interface{})
c := make(<- chan interface{})

6.channel使用中的死锁

对于一个已经阻塞的channel来说,如果继续发送或者接收,会出现死锁运行中的错误。

非缓冲区的只有发送或者接收,会出现阻塞,产生死锁

【go基础】go并发编程之channel_第7张图片

【go基础】go并发编程之channel_第8张图片

【go基础】go并发编程之channel_第9张图片

你可能感兴趣的:(数据结构)