Go-channel的关闭和广播

目录

一、不关闭channel会怎样

二、channel 的关闭

1、语法格式

2、channel关闭的特点

3、接收已关闭 channel 的值

4、标准 channel 格式


一、不关闭channel会怎样

我们先写一个数据生产者和数据消费者的程序,数据生产者不断生成数据,消费者不断消费生产者生产的数据。

package channel_close

import (
	"fmt"
	"sync"
	"testing"
)

//数据生产者
func dataProducer(ch chan int, wg *sync.WaitGroup) chan int {
	wg.Add(1)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
		}
		wg.Done()
	}()

	return ch
}

//数据消费者
func dataConsumer(ch chan int, wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		for i := 0; i < 10; i++ {
			data := <-ch
			fmt.Println(data)
		}
		wg.Done()
	}()
}

//数据消费者
func dataConsumer2(ch chan int, wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		for i := 0; i < 10; i++ {
			data := <-ch
			fmt.Println(data)
		}
		wg.Done()
	}()
}

//channel还未关闭的场景
func TestChannelNotClosed(t *testing.T) {
	ch := make(chan int)
	var wg sync.WaitGroup
	dataProducer(ch, &wg)
	dataConsumer(ch, &wg)
	wg.Wait()
}

一旦我们生产的数据和消费的数据不一致时,比如生产者可以生成 11 个数,消费者仍然只消费 10 个数,或者生产者生成 10 个数,而消费者去消费 11 个数时,就会报下面的错误:

fatal error: all goroutines are asleep - deadlock!

为了解决这种问题,Go 急需 channel 具有关闭功能,且关闭后会广播所有的订阅者。

二、channel 的关闭

1、语法格式

//关闭 channel
close(channelName)

//ok=true表示正常接收,false表示通道关闭
if val, ok := <-ch; ok {
    //other code
}

2、channel关闭的特点

  • 关闭的 channel 发送数据,会导致 panic
  • ok为bool值,true 表示正常接受,false 表示通道关闭
  • 所有的 channel 接收者都会在 channel 关闭时,立刻从阻塞等待中返回且 ok 值为 false。这个广播机制常被利用,进行向多个订阅者同时发送信号。如:退出信号。

3、接收已关闭 channel 的值

当 channel 已正常关闭,数据接收者还继续接收数据,则接收的数据为 channel 对应数据的默认值

package channel_close

import (
	"fmt"
	"sync"
	"testing"
)

//数据生产者
func dataProducer(ch chan int, wg *sync.WaitGroup) chan int {
	wg.Add(1)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
		}
        //关闭 channel
		close(ch)
		//向关闭的 channel 发送消息,会报 panic: send on closed channel
		//ch <- 11
		wg.Done()
	}()

	return ch
}

//数据消费者
func dataReceiver(ch chan int, wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
        //我们这里多接收一个数据,看看拿到的值是什么
		for i := 0; i < 11; i++ {
			data := <-ch
			fmt.Println(data)
		}
		wg.Done()
	}()
}

//关闭掉 channel
func TestCloseChannel(t *testing.T) {
	ch := make(chan int)
	var wg sync.WaitGroup
	dataProducer(ch, &wg)
	dataReceiver(ch, &wg)
	wg.Wait()
}

/*
=== RUN   TestCloseChannel
0
1
2
3
4
5
6
7
8
9
0    
--- PASS: TestCloseChannel (0.00s)
PASS
*/

我们会发现,当 channel 已关闭后,我们多接收了一个值,由于我们 channel 定义的数据类型为 int,则拿到的数据类型讲师 int 型的默认值 0。

4、标准 channel 格式

package channel_close

import (
	"fmt"
	"sync"
	"testing"
)

//数据生产者
func dataProducer(ch chan int, wg *sync.WaitGroup) chan int {
	wg.Add(1)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
		}
		//关闭 channel
		close(ch)
		wg.Done()
	}()

	return ch
}

//数据消费者
func dataConsumer(ch chan int, wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		for {
			if data, ok := <-ch; ok {
				fmt.Println(data)
			} else {
				break;
			}
		}
		wg.Done()
	}()
}

//数据消费者
func dataReceiver(ch chan int, wg *sync.WaitGroup) {
	wg.Add(1)
	go func() {
		for {
			if data, ok := <-ch; ok {
				fmt.Println(data)
			} else {
                //通道关闭后就退出
				break;
			}
		}
		wg.Done()
	}()
}

//关闭掉 channel
func TestCloseChannel(t *testing.T) {
	ch := make(chan int)
	var wg sync.WaitGroup
	//1个数据生成者
	dataProducer(ch, &wg)

	//多个数据消费者
	dataReceiver(ch, &wg)
	dataConsumer(ch, &wg)
	wg.Wait()
}


/*
=== RUN   TestCloseChannel
0
1
2
3
4
5
6
7
8
9
--- PASS: TestCloseChannel (0.00s)
PASS
*/

:这篇博文是我学习中的总结,如有转载请注明出处:

https://blog.csdn.net/DaiChuanrong/article/details/118353867

上一篇Go-多路选择和超时控制

下一篇Go-任务的取消

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值