首页 > 其他分享 >Go 快速入门指南 - 通道方向和关闭通道

Go 快速入门指南 - 通道方向和关闭通道

时间:2022-12-22 16:57:33浏览次数:35  
标签:ch func 入门 chan 关闭 Go main 通道

概述

建议先阅读 阻塞通道 和 非阻塞通道 小节。在前面的两个小节中, 为了最小化代码达到演示效果,省略了 关闭通道 的步骤, 正确的做法应该是在通道使用完成后关闭。

使用规则

通过关键字 clsoe 关闭通道。

  1. 1. 关闭一个空的通道 (值为 nil) 时,panic

  2. 2. 关闭一个非空 && 已关闭的通道时,panic

  3. 3. 关闭一个非空 && 未关闭的通道时,正常关闭

这里的规则不必死记硬背,笔者遇到的大多数情况属于第二种,也就是 重复关闭一个通道, 读者做到实际开发中遇到 关闭通道 的场景时,联系上下文,确认通道不会出现重复关闭的情况即可。

例子

关闭一个空的通道

package main

func main() {
    var ch chan bool
    close(ch)
}

// $ go run main.go
// 输出如下
/**
  panic: close of nil channel

  ...
  ...
  exit status 2
*/

关闭一个非空 && 已关闭通道

package main

func main() {
    ch := make(chan bool)
    close(ch)
    close(ch) // 重复关闭
}

// $ go run main.go
// 输出如下
/**
  panic: close of nil channel

  ...
  ...
  exit status 2
*/

关闭一个非空 && 未关闭的通道

package main

func main() {
    ch := make(chan bool)
    close(ch)
    println("channel closed")
}

// $ go run main.go
// 输出如下
/**
  channel closed
*/

通道方向

通道的方向分为 发送 和 接收。默认情况下,通道是双向的 (同时发送和接收),但是可以通过标识符指明通道为单向 (只读或只写)。

语法规则

可读写通道 (同时支持发送和接收)

变量 := make(chan 数据类型)
# 例子
ch := make(chan string)

只读通道 (只支持接收)

变量 := make(<-chan 数据类型)
# 例子
ch := make(<-chan string)

只写通道 (只支持发送)

变量 := make(chan<- 数据类型)
# 例子
ch := make(chan<- string)

类型转换

双向通道可以转换为单向通道,但是单向通道无法转换为双向通道。

例子

package main

// 参数是一个写入通道
func ping(pings chan<- string) {
    //<-pings     // 错误: pings 通道只能写入
    pings <- "hello world"
}

func pong(pings <-chan string, pongs chan<- string) {
    //pings <- "hello world" // 错误: pings 通道只能读取
    //<-pongs      // 错误: pongs 通道只能写入

    msg := <-pings
    pongs <- msg
}

func main() {
    pings := make(chan string)
    pongs := make(chan string)
    done := make(chan bool)

    go ping(pings)
    go pong(pings, pongs)

    go func() {
        msg := <-pongs
        println(msg)
        done <- true
    }()

    <-done

    close(pings)
    close(pongs)
    close(done)
}

// $ go run main.go
// 输出如下
/**
  hello world
*/

检测通道是否关闭

Go 语言没有提供函数或方法判断一个通道是否关闭。因此只能使用一个变通的办法:接收通道元素,根据返回的布尔值确定通道是否关闭。

例子

双向通道检测

package main

func main() {
    ch := make(chan string)
    close(ch)

    if _, open := <-ch; !open {
        println("channel closed")
    }
}

// $ go run main.go
// 输出如下
/**
  channel closed
*/

单向 (只读) 通道检测

package main

import "time"

func main() {
    ch := make(chan string)

    go func(c <-chan string) {
        if _, open := <-c; !open {
            println("channel closed")
        }
    }(ch)

    close(ch)
    time.Sleep(time.Second)
}

// $ go run main.go
// 输出如下
/**
  channel closed
*/

单向 (只写) 通道检测

对于只写通道,需要采用一个折中的办法:

  • • 尝试向通道写入数据

  • • 如果写入成功,说明通道还未关闭

  • • 写入失败,发生 panic, 这时可以利用 defer 在 recover 中输出原因

package main

import "time"

func main() {
    ch := make(chan string)

    go func(c chan<- string) {
        defer func() {
            if err := recover(); err != nil { // 捕获到 panic
                println("channel closed")
            }
        }()

        c <- "hello world"
    }(ch)

    close(ch)
    time.Sleep(time.Second)
}

// $ go run main.go
// 输出如下,你的输出可能和这里的不一样
/**
  channel closed
*/

联系我

公众号

标签:ch,func,入门,chan,关闭,Go,main,通道
From: https://www.cnblogs.com/duanbiaowu/p/16998878.html

相关文章

  • Go 快速入门指南 - 互斥锁和定时器
    互斥锁对于任一共享资源,同一时间保证只有一个操作者,这种方法称为 互斥机制。关键字 Mutex 表示互斥锁类型,它的 Lock 方法用于获取锁,Unlock 方法用于释放锁。在 Lo......
  • Go 快速入门指南 - 遍历通道
    概述建议先阅读 range, 阻塞通道, 非阻塞通道 等小节。range 除了可以遍历字符串、切片、数组等数据结构外,还可以遍历通道。语法规则和遍历其他数据结构不同,遍历......
  • Go 快速入门指南 - 结构体
    概述结构体 是将零个或多个字段(变量)组合在一起的复合数据类型,类似于面向对象语言中的 对象。结构体以及其字段都使用 可见性 规则。语法规则type 结构体名称......
  • Go 快速入门指南 - 嵌套结构体
    概述Go支持将多个结构体通过嵌套的方式,组成一个大的结构体,降低了单个结构体复杂度,同时提高了结构体之间组合的灵活性。例子为了省略篇幅,本小节只使用 字面量 方式初......
  • Go 快速入门指南 - 函数
    概述函数 是将一个或者一类问题包装为一个代码块,可以被多次调用,提高代码重用性。Go函数中声明、定义、参数、返回值这些基础概念,和其他编程语言中的一致,这里不再赘述。......
  • Go 快速入门指南 - 接口和方法
    接口Go接口是隐式实现。 对于一个数据类型,无需声明它实现了哪些接口,只需要实现接口必需的方法即可。当然了,存在一个小问题就是:我们可能无意间实现了某个接口:),所以 ......
  • Go 快速入门指南 - init 函数
    概述init()函数 是一个特殊的函数,一般称为初始化函数,不能被调用。 在每个文件里面,当程序启动或者文件被作为包引用的时候,init()函数就会自动执行,一般用来做一些包的......
  • Go 快速入门指南 - 零值和错误
    零值当一个变量使用 var 进行声明后并未进行初始化(变量后面没有赋值符 =)操作,会默认分配一个零值(zerovalue)。不同类型对应的零值类型零值boolfalse......
  • Go 快速入门指南 - 判断是否实现接口
    判断是否实现接口Go里面没有 implements 关键字来判断一个结构体(对象)是否实现了某个接口, 要实现类似的功能,需要用到 类型断言。类型断言// 判断 v 是否实......
  • Go 快速入门指南 - 字符切片
    概述建议先阅读 字符串, 切片 两个小节。由于字符串不可变,如果每次以 重新赋值 的方式改变字符串,效率会非常低,这时应该使用 []byte 类型,[]byte元素可以被修改。......