首页 > 其他分享 >Go_Channel详解

Go_Channel详解

时间:2022-10-10 13:35:25浏览次数:35  
标签:ch int goroutine chan 详解 Channel Go channel 通道

一 channel介绍

单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。

虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题。

Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。

如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。

Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

二 channel类型

channel是一种类型,一种引用类型。声明通道类型的格式如下:

    var 变量 chan 元素类型

举几个例子:

    var ch1 chan int   // 声明一个传递整型的通道
    var ch2 chan bool  // 声明一个传递布尔型的通道
    var ch3 chan []int // 声明一个传递int切片的通道

三 创建channel

通道是引用类型,通道类型的空值是nil。

var ch chan int
fmt.Println(ch) // <nil>

声明的通道后需要使用make函数初始化之后才能使用。

创建channel的格式如下:

make(chan 元素类型, [缓冲大小])

channel的缓冲大小是可选的。

举几个例子:

ch4 := make(chan int)
ch5 := make(chan bool)
ch6 := make(chan []int)

四 channel操作

通道有发送(send)、接收(receive)和关闭(close)三种操作。

发送和接收都使用<-符号。

现在我们先使用以下语句定义一个通道:

ch := make(chan int)

发送

将一个值发送到通道中。

ch <- 10 // 把10发送到ch中

接收

从一个通道中接收值。

x := <- ch // 从ch中接收值并赋值给变量x
<-ch       // 从ch中接收值,忽略结果

关闭

我们通过调用内置的close函数来关闭通道。

close(ch)

关于关闭通道需要注意的事情是,只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。

关闭后的通道有以下特点:

1.对一个关闭的通道再发送值就会导致panic。
2.对一个关闭的通道进行接收会一直获取值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。

五 无缓冲的通道

在这里插入图片描述
无缓冲的通道又称为阻塞的通道。我们来看一下下面的代码:

func main() {
    ch := make(chan int)
    ch <- 10
    fmt.Println("发送成功")
}

上面这段代码能够通过编译,但是执行的时候会出现以下错误:

 fatal error: all goroutines are asleep - deadlock!

    goroutine 1 [chan send]:
    main.main()
            .../src/github.com/pprof/studygo/day06/channel02/main.go:8 +0x54

为什么会出现deadlock错误呢?

因为我们使用ch := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点,快递员给你打电话必须要把这个物品送到你的手中,简单来说就是无缓冲的通道必须有接收才能发送。

上面的代码会阻塞在ch <- 10这一行代码形成死锁,那如何解决这个问题呢?

一种方法是启用一个goroutine去接收值,例如:

func recv(c chan int) {
    ret := <-c
    fmt.Println("接收成功", ret)
}
func main() {
    ch := make(chan int)
    go recv(ch) // 启用goroutine从通道接收值
    ch <- 10
    fmt.Println("发送成功")
}

无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。

使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。

六 有缓冲的通道

解决上面问题的方法还有一种就是使用有缓冲区的通道。
在这里插入图片描述
我们可以在使用make函数初始化通道的时候为其指定通道的容量,例如:

func main() {
    ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
    ch <- 10
    fmt.Println("发送成功")
}

只要通道的容量大于零,那么该通道就是有缓冲的通道,通道的容量表示通道中能存放元素的数量。就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。

我们可以使用内置的len函数获取通道内元素的数量,使用cap函数获取通道的容量,虽然我们很少会这么做。

七 close()

可以通过内置的close()函数关闭channel(如果你的管道不往里存值或者取值的时候一定记得关闭管道)

package main

import "fmt"

func main() {
    c := make(chan int)
    go func() {
        for i := 0; i < 5; i++ {
            c <- i
        }
        close(c)
    }()
    for {
        if data, ok := <-c; ok {
            fmt.Println(data)
        } else {
            break
        }
    }
    fmt.Println("main结束")
}

八 通道总结

channel常见的异常总结,如下图:
在这里插入图片描述
注意:关闭已经关闭的channel也会引发panic。

标签:ch,int,goroutine,chan,详解,Channel,Go,channel,通道
From: https://www.cnblogs.com/qi66/p/16775365.html

相关文章

  • Go_IO操作
    1.输入输出的底层原理终端其实是一个文件,相关实例如下:os.Stdin:标准输入的文件实例,类型为*Fileos.Stdout:标准输出的文件实例,类型为*Fileos.Stderr:标准错误输出的文件实......
  • 37、linux下安装python3.6和django
    37.1、安装python:1、python介绍:python是一种面向对象的,解释型的计算机语言,它的特点是语法简介,优雅,简单易学。1989年诞生,Guido(龟叔)开发。编译型语言:代码在编译之后,编译成......
  • Go_Goroutine详解
    Goroutine详解goroutine的概念类似于线程,但goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将goroutine中的任务合理地分配给每个CPU。Go语言之所以被称......
  • 49、django工程(cookie+session)
    49.1、介绍:1、cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生。cookie的工作原理是,由服务器产......
  • Trigonometric functions
    Trigonometricfunctions标签(空格分隔):三角函数Unitright-angletriangle{#1}\(\sin\theta=\frac{opposite\quadside}{hypotenuse}\)\(\cos\theta=\frac{adjacent......
  • 46、django工程(view)
    46.1、djangoview视图函数说明:1、http请求中产生两个核心对象:(1)http请求:HttpRequest对象。(2)http响应:HttpResponse对象。2、views函数是接收用户请求,处理业务逻辑的函数:46.......
  • Argo CD系列视频图文版之数据加密
    配套视频往期回顾1.​​ArgoCD系列之初识ArgoCD​​2.​​ArgoCD系列之ArgoCD环境搭建​​3.​​ArgoCD系列之安装ArgoCD​​4.​​ArgoCD系列之自建应用模拟开发场......
  • 一文详解 | 低代码发展的 “背后推手”
    近年来,随着数字经济的发展,越来越多企业迈入了数字化转型的行列,低代码平台因为其低门槛、高效率的特性,被许多企业认为是数字化转型的利器,因而得到了长足发展。低代码市场快......
  • 详解数仓的锁相关参数及视图
    摘要:GaussDB(DWS)中锁等待可以设置等待超时相关参数,一旦等锁的时间超过参数配置值会抛错。本文分享自华为云社区《​​GaussDB(DWS)锁相关参数及视图详解​​》,作者:yd_22......
  • 11. JS switch case语句详解
    1.前言JSswitchcase语句与 ifelse 语句的多分支结构类似,都可以根据不同的条件来执行不同的代码;但是与ifelse多分支结构相比,switchcase语句更加简洁和紧凑,执行......