Golang优雅关闭channel的方法示例

前言

最近使用go开发后端服务,服务关闭需要保证channel中的数据都被读取完,理由很简单,在收到系统的中断信号后,系统需要做收尾工作,保证channel的数据都要被处理掉,然后才可以关闭系统。但实现起来没那么简单,下面来一起看看详细的介绍吧。

关于Go channel设计和规范的批评:

  • 在不能更改channel状态的情况下,没有简单普遍的方式来检查channel是否已经关闭了
  • 关闭已经关闭的channel会导致panic,所以在closer(关闭者)不知道channel是否已经关闭的情况下去关闭channel是很危险的
  • 发送值到已经关闭的channel会导致panic,所以如果sender(发送者)在不知道channel是否已经关闭的情况下去向channel发送值是很危险的

所以Golang 内建的 close 方法可以关闭 channel,如果往已经关闭的 channel 发送数据,则会报错:panic: close of closed channel.

看如下代码,在一段时间内,生产者可以不断往 channel 写入数据,消费者进行处理,一段时间后 channel 关闭了,这个时候如果还有数据往 channel 发送,程序就会报错。

package main

import (
 "fmt"
 "sync"
 "time"
)

func main() {
 jobs := make(chan int)
 var wg sync.WaitGroup
 go func() {
 time.Sleep(time.Second * 3)
 close(jobs)
 }()
 go func() {
 for i := 0; ; i++ {
 jobs <- i
 fmt.Println("produce:", i)
 }
 }()
 wg.Add(1)
 go func() {
 defer wg.Done()
 for i := range jobs {
 fmt.Println("consume:", i)
 }
 }()
 wg.Wait()
}

多运行几次出错的概率会比较大:

produce: 33334
consume: 33334
consume: 33335
produce: 33335
produce: 33336
consume: 33336
consume: 33337
produce: 33337
produce: 33338
consume: 33338
consume: 33339
produce: 33339
produce: 33340
consume: 33340
panic: send on closed channel

goroutine 19 [running]:
panic(0x49b660, 0xc042410bb0)
  C:/Go/src/runtime/panic.go:500 +0x1af
main.main.func2(0xc04203a180)
  C:/Users/tanteng/Go/src/examples/channel_close.go:18 +0x6b
created by main.main
  C:/Users/tanteng/Go/src/examples/channel_close.go:21 +0xb8
exit status 2

如何优雅关闭 channel

那么在往通道发数据前如何判断通道是否关闭呢?

1._,ok := <- jobs

此时如果 channel 关闭,ok 值为 false,如果 channel 没有关闭,则会漏掉一个 jobs

2.使用 select 方式

再创建一个 channel,叫做 timeout,如果超时往这个 channel 发送 true,在生产者发送数据给 jobs 的 channel,用 select 监听 timeout,如果超时则关闭 jobs 的 channel.

完整代码如下:

package main

import (
 "fmt"
 "sync"
 "time"
)

func main() {
 jobs := make(chan int)
 timeout := make(chan bool)
 var wg sync.WaitGroup
 go func() {
 time.Sleep(time.Second * 3)
 timeout <- true
 }()
 go func() {
 for i := 0; ; i++ {
 select {
 case <-timeout:
 close(jobs)
 return

 default:
 jobs <- i
 fmt.Println("produce:", i)
 }
 }
 }()
 wg.Add(1)
 go func() {
 defer wg.Done()
 for i := range jobs {
 fmt.Println("consume:", i)
 }
 }()
 wg.Wait()
}

这样就可以保证不会往已经关闭的 channel 中发送数据了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Go语言的管道Channel用法实例

    本文实例讲述了Go语言的管道Channel用法.分享给大家供大家参考.具体分析如下: channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值. ch <- v // 将 v 送入 channel ch. v := <-ch // 从 ch 接收,并且赋值给 v. ("箭头"就是数据流的方向.) 和 map 与 slice 一样,channel 使用前必须创建: ch := make(chan int) 默认情况下,在另一端准备好之前,

  • Golang中channel使用的一些小技巧

    关闭2次 复制代码 代码如下: ch := make(chan bool) close(ch) close(ch)  // 这样会panic的,channel不能close两次 读取的时候channel提前关闭了 复制代码 代码如下: ch := make(chan string) close(ch) i := <- ch // 不会panic, i读取到的值是空 "",  如果channel是bool的,那么读取到的是false 向已经关闭的channel写数据 复制代码 代码

  • go语言channel实现多核并行化运行的方法

    本文实例讲述了go语言channel实现多核并行化运行的方法.分享给大家供大家参考.具体如下: 这里定义一个Add函数,用于返回两个整数的和,使用go 语句进行并行化运算,为了等待各个并行运算结束获得其返回值,需要引入channel 复制代码 代码如下: package main import "fmt" func Add(x int,y int,channel chan int) {     sum := library.Add(x,y)     fmt.Println(sum)  

  • Go语言中使用 buffered channel 实现线程安全的 pool

    概述 我们已经知道 Go 语言提供了 sync.Pool,但是做的不怎么好,所以有必要自己来实现一个 pool. 给我看代码: 复制代码 代码如下: type Pool struct {   pool chan *Client } // 创建一个新的 pool func NewPool(max int) *Pool {   return &Pool{     pool: make(chan *Client, max),   } } // 从 pool 里借一个 Client func (p *P

  • golang中单向channel的语法介绍

    本文主要给大家介绍的是关于golang单向channel语法的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 今天闲来无事补充一下golang的语法知识,想起来看看context的用法,结果碰到了一个没见过的channel语法: // A Context carries a deadline, cancelation signal, and request-scoped values // across API boundaries. Its methods are sa

  • golang判断chan channel是否关闭的方法

    本文实例讲述了golang判断chan channel是否关闭的方法.分享给大家供大家参考,具体如下: 群里有朋友问,怎么判断chan是否关闭,因为close的channel不会阻塞,并返回类型的nil值,会导致死循环.在这里写个例子记录一下,并且分享给大家 如果不判断chan是否关闭 Notice: 以下代码会产生死循环 复制代码 代码如下: package main import (     "fmt" ) func main() {     c := make(chan int,

  • Golang优雅关闭channel的方法示例

    前言 最近使用go开发后端服务,服务关闭需要保证channel中的数据都被读取完,理由很简单,在收到系统的中断信号后,系统需要做收尾工作,保证channel的数据都要被处理掉,然后才可以关闭系统.但实现起来没那么简单,下面来一起看看详细的介绍吧. 关于Go channel设计和规范的批评: 在不能更改channel状态的情况下,没有简单普遍的方式来检查channel是否已经关闭了 关闭已经关闭的channel会导致panic,所以在closer(关闭者)不知道channel是否已经关闭的情况下去

  • Golang 统计字符串字数的方法示例

    比如新浪微博发微博的输入框有一个已输入字数的统计,它的规则推测是:汉字和中文标点算 1 个字数,英文和其他符号算 0.5 个字数.不足 1 个字算 1 个.大家可以去微博体验一下计算方式. golang 可以使用正则和 unicode 包的方法判断. 以下函数 GetStrLength 返回输入的字符串的字数,每个汉字和中文标点算 1 个字数,英文和其他字符算半个字数,不足 1 个字算 1 个. // GetStrLength 返回输入的字符串的字数,汉字和中文标点算 1 个字数,英文和其他字符

  • 在Golang中使用Redis的方法示例

    周五上班的主要任务是在公司老平台上用redis处理一个队列问题,顺便复习了一下redis操作的基础知识,回来后就想着在自己的博客demo里,用redis来优化一些使用场景,学习一下golang开发下redis的使用. Redis简单介绍 简介 关于Redis的讨论,其实在现在的后台开发中已经是个老生常谈的问题,基本上也是后端开发面试的基本考察点.其中 Redis的背景介绍和细节说明在这里就不赘述.不管怎么介绍,核心在于Redis是一个基于内存的key-value的多数据结构存储,并可以提供持久化

  • 利用Golang解析json数据的方法示例

    本文主要给大家介绍的是关于Golang解析json数据的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 使用 Golang 解析 json 数据,这种 json 格式是对象的数组,官方文档有一个示例: var jsonBlob = []byte(`[ {"Name": "Platypus", "Order": "Monotremata"}, {"Name": "Quoll

  • Golang中List的实现方法示例详解

    前言 为了快速回顾Go基本的语法知识,打算用Go中的基本语法以及特性来实现一些常见的数据结构和排序算法,通过分析如何实现一些基本的数据结构,可以很快学习Go的语法特性.记忆更加深刻,掌握更加迅速.这是我认为学习一门新语言入门最好的方式.这也是方便自己以后需要用Go来写东西的一种前期准备,到时候就不用去翻一些教程了.系列博文的第一篇就从如何实现List开始. 需求 大家都知道基本链表得有以下特性:链表的初始化.链表的长度.节点的插入.删除.查找等一些常见的基本操作,最后写好之后,需要测试.关于测试

  • golang顺时针打印矩阵的方法示例

    题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10. package main import "fmt" func main() { //s := [][]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13,

  • 使用golang写一个redis-cli的方法示例

    0. redis通信协议 redis的客户端(redis-cli)和服务端(redis-server)的通信是建立在tcp连接之上, 两者之间数据传输的编码解码方式就是所谓的redis通信协议.所以,只要我们的redis-cli实现了这个协议的解析和编码,那么我们就可以完成所有的redis操作. redis 协议设计的非常易读,也易于实现,关于具体的redis通信协议请参考:通信协议(protocol).后面我们在实现这个协议的过程中也会简单重复介绍一下具体实现 1. 建立tcp连接 redis

  • Golang通过小程序获取微信openid的方法示例

    为什么要获取小程序的 openid 在开发微信小程序的过程中,小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系.那么这个用户身份标识就是 openid. 小程序获取 openid 的流程 那么小程序获取 openid 的流程具体如下,这里我简化了一下,因为我们只需要获取到 openid 即可,具体可以参考 这里 我们需要在小程序中调用 wx.login() 获取 code 码,然后将这个 code 码发送给后端,后端带着这个 code 码和 app

  • golang如何优雅的编写事务代码示例

    前言 新手程序员大概有如下特点 if嵌套经常超过3层.经常出现重复代码.单个函数代码特别长. 只会crud,对语言特性和语言的边界不了解. 不懂面向对象原则和设计模式,以为copy代码就算学会了,常见的是代码职责不明确或者写出万能类 不知道数据结构和算法的重要性,以为靠硬件能解决运行慢的问题 架构不懂,搭建框架不会,搭建环境不会,使用的软件底层原理一问三不知 其实吧,很多人干了很多年,看似是老手,平时工作看似很忙,其实做的都是最简单的活. 这就像去锻炼,有的人每天练的很积极,准时打卡,频繁发朋友

随机推荐