前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 语言并发编程系列(八)—— 通道类型篇:错误和异常处理

Go 语言并发编程系列(八)—— 通道类型篇:错误和异常处理

作者头像
学院君
发布2019-09-04 16:50:38
7860
发布2019-09-04 16:50:38
举报
文章被收录于专栏:学院君的专栏

在前面几篇通道教程中,我们陆续介绍了与通道相关的基本语法、单向通道以及 select 语句,有关通道的基本知识就介绍到这里,今天我们来看下通道使用过程中的错误和异常处理。

在并发编程的通信过程中,最需要处理的就是超时问题:比如向通道发送数据时发现通道已满,或者从通道接收数据时发现通道为空。如果不正确处理这些情况,很可能会导致整个协程阻塞并产生死锁。此外,如果我们试图向一个已经关闭的通道发送数据或关闭已经关闭的通道,也会引发 panic。以上都是我们在使用通道进行并发通信时需要尤其注意的。

接下来我们来看看如何解决上述问题。

超时处理机制实现

Go 语言没有提供直接的超时处理机制,但我们可以借助 select 语句来实现类似机制解决超时问题,因为 select语句的特点是只要其中一个 case 对应的通道操作已经完成,程序就会继续往下执行,而不会考虑其他 case 的情况。基于此特性,我们来为通道操作实现超时处理机制,创建一个新的 Go 文件 channel5.go,并编写代码如下:

代码语言:javascript
复制
package main

import (
    "fmt"
    "time"
)

func main()  {
    // 初始化 ch 通道
    ch := make(chan int, 1)

    // 初始化 timeout 通道
    timeout := make(chan bool, 1)

    // 实现一个匿名超时等待函数
    go func() {
        time.Sleep(1e9)  // 睡眠1秒钟
        timeout <- true
    }()

    // 借助 timeout 通道结合 select 语句实现 ch 通道读取超时效果
    select {
        case <- ch:
            fmt.Println("接收到 ch 通道数据")
        case <- timeout:
            fmt.Println("超时1秒,程序退出")
    }
}

使用 select 语句可以避免永久等待的问题,因为程序会在从 timeout 通道中接收到数据后继续执行,无论对 ch的读取是否还处于等待状态,从而实现 1 秒超时的效果。这种写法看起来是一个编程小技巧,但却是在 Go 语言并发编程中避免通道通信超时的最有效方法。

执行上述代码,打印结果如下:

代码语言:javascript
复制
超时1秒,程序退出

而如果没有 timeout 通道和上述 select 机制,从 ch 通道接收数据会得到如下 panic(死锁):

代码语言:javascript
复制
fatal error: all goroutines are asleep - deadlock!

避免对已关闭通道进行操作

为了避免对已关闭通道再度执行关闭操作引发 panic,一般我们约定只能在发送方关闭通道,而在接收方,我们则通过通道接收操作返回的第二个参数是否为 false 判定通道是否已经关闭,如果已经关闭,则不再执行发送操作,示例代码 channel6.go 如下:

代码语言:javascript
复制
package main

import "fmt"

func main()  {
    ch := make(chan int, 2)
    // 发送方
    go func() {
        for i := 0; i < 5; i++ {
            fmt.Printf("发送方: 发送数据 %v...\n", i)
            ch <- i
        }
        fmt.Println("发送方: 关闭通道...")
        close(ch)
    }()
    // 接收方
    for {
        num, ok := <-ch
        if !ok {
            fmt.Println("接收方: 通道已关闭")
            break
        }
        fmt.Printf("接收方: 接收数据: %v\n", num)
    }
    fmt.Println("程序退出")
}

上述代码执行结果如下:

如果我们试图在通道 ch 关闭后发送数据到该通道,则会得到如下 panic:

代码语言:javascript
复制
panic: send on closed channel

而如果我们试图在通道 ch 关闭后再次关闭它,则会得到如下 panic:

代码语言:javascript
复制
panic: close of closed channel
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 极客书房 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 超时处理机制实现
  • 避免对已关闭通道进行操作
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档