关键词:
Go语言通道(chan)——goroutine之间通信的管道
如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制。一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。
Go语言提倡使用通信的方法代替共享内存,当一个资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。
这里通信的方法就是使用通道(channel),如下图所示。
图:goroutine 与 channel 的通信
在地铁站、食堂、洗手间等公共场所人很多的情况下,大家养成了排队的习惯,目的也是避免拥挤、插队导致的低效的资源使用和交换过程。代码与数据也是如此,多个 goroutine 为了争抢数据,势必造成执行的低效率,使用队列的方式是最高效的,channel 就是一种队列一样的结构。
通道的特性
Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。
通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。
声明通道类型
通道本身需要一个类型进行修饰,就像切片类型需要标识元素类型。通道的元素类型就是在其内部传输的数据类型,声明如下:
var 通道变量 chan 通道类型
- 通道类型:通道内的数据类型。
- 通道变量:保存通道的变量。
chan 类型的空值是 nil,声明后需要配合 make 后才能使用。
创建通道
通道是引用类型,需要使用 make 进行创建,格式如下:
通道实例 := make(chan 数据类型)
- 数据类型:通道内传输的元素类型。
- 通道实例:通过make创建的通道句柄。
请看下面的例子:
- ch1 := make(chan int) // 创建一个整型类型的通道
- ch2 := make(chan interface) // 创建一个空接口类型的通道, 可以存放任意格式
- type Equip struct /* 一些字段 */
- ch2 := make(chan *Equip) // 创建Equip指针类型的通道, 可以存放*Equip
使用通道发送数据
通道创建后,就可以使用通道进行发送和接收操作。
1) 通道发送数据的格式
通道的发送使用特殊的操作符<-
,将数据通过通道发送的格式为:
通道变量 <- 值
- 通道变量:通过make创建好的通道实例。
- 值:可以是变量、常量、表达式或者函数返回值等。值的类型必须与ch通道的元素类型一致。
2) 通过通道发送数据的例子
使用 make 创建一个通道后,就可以使用<-
向通道发送数据,代码如下:
- // 创建一个空接口通道
- ch := make(chan interface)
- // 将0放入通道中
- ch <- 0
- // 将hello字符串放入通道中
- ch <- "hello"
3) 发送将持续阻塞直到数据被接收
把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。Go 程序运行时能智能地发现一些永远无法发送成功的语句并做出提示,代码如下:
- package main
- func main()
- // 创建一个整型通道
- ch := make(chan int)
- // 尝试将0通过通道发送
- ch <- 0
运行代码,报错:
fatal error: all goroutines are asleep - deadlock!
报错的意思是:运行时发现所有的 goroutine(包括main)都处于等待 goroutine。也就是说所有 goroutine 中的 channel 并没有形成发送和接收对应的代码。
使用通道接收数据
通道接收同样使用<-
操作符,通道接收有如下特性:
① 通道的收发操作在不同的两个 goroutine 间进行。
由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。
② 接收将持续阻塞直到发送方发送数据。
如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。
③ 每次接收一个元素。
通道一次只能接收一个数据元素。
通道的数据接收一共有以下 4 种写法。
1) 阻塞接收数据
阻塞模式接收数据时,将接收变量作为<-
操作符的左值,格式如下:
data := <-ch
执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
2) 非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
data, ok := <-ch
- data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
- ok:表示是否接收到数据。
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行,可以参见后面的内容。
3) 接收任意数据,忽略接收的数据
阻塞接收数据后,忽略从通道返回的数据,格式如下:
<-ch
执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。
使用通道做并发同步的写法,可以参考下面的例子:
- package main
- import (
- "fmt"
- )
- func main()
- // 构建一个通道
- ch := make(chan int)
- // 开启一个并发匿名函数
- go func()
- fmt.Println("start goroutine")
- // 通过通道通知main的goroutine
- ch <- 0
- fmt.Println("exit goroutine")
- ()
- fmt.Println("wait goroutine")
- // 等待匿名goroutine
- <-ch
- fmt.Println("all done")
执行代码,输出如下:
wait goroutine
start goroutine
exit goroutine
all done
代码说明如下:
- 第 10 行,构建一个同步用的通道。
- 第 13 行,开启一个匿名函数的并发。
- 第 18 行,匿名 goroutine 即将结束时,通过通道通知 main 的 goroutine,这一句会一直阻塞直到 main 的 goroutine 接收为止。
- 第 27 行,开启 goroutine 后,马上通过通道等待匿名 goroutine 结束。
4) 循环接收
通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下:
- for data := range ch
通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。
遍历通道数据的例子请参考下面的代码。
使用 for 从通道中接收数据:
- package main
- import (
- "fmt"
- "time"
- )
- func main()
- // 构建一个通道
- ch := make(chan int)
- // 开启一个并发匿名函数
- go func()
- // 从3循环到0
- for i := 3; i >= 0; i--
- // 发送3到0之间的数值
- ch <- i
- // 每次发送完时等待
- time.Sleep(time.Second)
- ()
- // 遍历接收通道数据
- for data := range ch
- // 打印通道数据
- fmt.Println(data)
- // 当遇到数据0时, 退出接收循环
- if data == 0
- break
执行代码,输出如下:
3
2
1
0
代码说明如下:
- 第 12 行,通过 make 生成一个整型元素的通道。
- 第 15 行,将匿名函数并发执行。
- 第 18 行,用循环生成 3 到 0 之间的数值。
- 第 21 行,将 3 到 0 之间的数值依次发送到通道 ch 中。
- 第 24 行,每次发送后暂停 1 秒。
- 第 30 行,使用 for 从通道中接收数据。
- 第 33 行,将接收到的数据打印出来。
- 第 36 行,当接收到数值 0 时,停止接收。如果继续发送,由于接收 goroutine 已经退出,没有 goroutine 发送到通道,因此运行时将会触发宕机报错。
go-通道(代码片段)
...l)是Go语言中一种非常独特的数据结构。它可用于在不同Goroutine之间传递类型化的数据,并且是并发安全的。 通道类型的表示方法很简单,仅由两部分组成chanT在这个类型字面量中,左边是代表通道类型的关键字chan,而右边... 查看详情
go语言:一个简单的goroutine同步(synchronize)
我们可以使用channels在多个goroutine之间进行同步(synchronize),下面直接看例子。例子1,使用chan等待一个goroutine结束。packagemainimport"fmt"import"time"//在这定义一个函数,以goroutine的方式运行。使用done这个chan来通知//其... 查看详情
go_channel
通道可以被认为是Goroutines通信的管道。类似于管道中的水从一端到另一端的流动,数据可以从一端发送到另一端,通过通道接收。在前面讲Go语言的并发时候,我们就说过,当多个Goroutine想实现共享数据的时候,虽然也提供了传... 查看详情
go语言学习笔记—进阶—并发编程:通道(channel)——在多个goroutine之间通信的管道(代码片段)
...用“共享内存”的方式交换数据,但共享内存在不同goroutine间易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存加锁,这会增加额外的时间开销。go使用通道channel来实现goroutine之间的通信。在食堂... 查看详情
go笔记(十四):通道channel(代码片段)
1、通道 通道channel是Go提供的一种用于各个协程(goroutine)之间的数据共享,保证数据同步交换的机制。协程是轻量级线程,类似于Java中的线程。2、通道的类型2.1、无缓冲通道 用于同步通信,可保证在发送和接收数据时完... 查看详情
go语言之通道
...,Go又为我们提供了一种工具,这就是通道。所以在多个goroutine并发中,我们不仅可以通过原子函数和互斥锁保证对共享资源的安全访问,消除竞争的状态,还可以通过使用通道,在多个goroutine发送和接受共享的数据,达到数据... 查看详情
gochannel实现原理分析
参考技术Achannel一个类型管道,通过它可以在goroutine之间发送和接收消息。它是Golang在语言层面提供的goroutine间的通信方式。Go依赖于成为CSP的并发模型,通过Channel实现这种同步模式。Golang并发的核心哲学是不要通过共享内存进... 查看详情
golang入门到项目实战golang并发变成之通道channel
参考技术AGo提供了一种称为通道的机制,用于在goroutine之间共享数据。当您作为goroutine执行并发活动时,需要在goroutine之间共享资源或数据,通道充当goroutine之间的管道(管道)并提供一种机制来保证同步交换。根据数据交换的... 查看详情
gochannel概述
...,也是引用类型,零值为nil 特性同一时刻,仅有一个goroutine可以向该通道发送元素值,同时也仅有一个goroutine可以从该通道接收元素值。通道中的元素值,严格按发送到该通道的先后顺序排列,最先发送到的元素值,一定最... 查看详情
goplgoroutine和通道(代码片段)
Go语言的并发编程风格Go有两种并发编程风格:goroutine和通道(chennle),支持通信顺序进程(CommunicatingSequentialProcess,CSP),CSP是一个并发的模式,在不同的执行体(goroutine)之间传递值,但是变量本身局限于单一的执行体。共享... 查看详情
go语言并发与通道的运用(代码片段)
在go语言中我们可以使用goroutine开启并发。goroutine是轻量级线程,goroutine的调度是由Golang运行时进行管理的。goroutine语法格式:go函数名(参数列表)实例1:packagemainimport("fmt""time")funcsay(sstring)fori:=0;i< 查看详情
go语言goroutines并发模式
并发模式让我们先来回顾一下boring函数的例子。func boring(msg string, c chan string) { for i := 0; ; i++ { &n 查看详情
如果通过 Golang 通道发送,结构是不是实际上在 goroutine 之间复制?
】如果通过Golang通道发送,结构是不是实际上在goroutine之间复制?【英文标题】:IsastructactuallycopiedbetweengoroutinesifsentoveraGolangchannel?如果通过Golang通道发送,结构是否实际上在goroutine之间复制?【发布时间】:2016-06-2102:42:27【问... 查看详情
七天入门go语言通道&goroutine|第四天并发编程(代码片段)
1.前言在go社区有这样一句话不要通过共享内存来通信,而是通过通信来共享内存。go官方是建议使用管道通信的方式来进行并发。通道是用于协程间交流的通信载体。严格地来说,通道就是数据传输的管道,数据通过... 查看详情
go源码阅读——chan.go(代码片段)
【博文目录>>>】【项目地址>>>】chan.go是go语言通道实现,通道结构的定义,接收和发送的操作都此文件中实现。通道的结构hchan是通道表示的基本结构,其内容表示如下:一些特殊情况当dataqsiz=0时... 查看详情
go语言之并发示例-pool
...的通道实现一个资源池,这个资源池可以管理在任意多个goroutine之间共享的资源,比如网络连接、数据库连接等,我们在数据库操作的时候,比较常见的就是数据连接池,也可以基于我们实现的资源池来实现。可以看出,资源池... 查看详情
go语言之通道(代码片段)
1packagemain23import(4"fmt"5)67funcmain()8//channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。9//声明通道类型10varachanint//声明一个int类型的通道,声明之后需要对它进行初始化11fmt.Println(a)12ch:=make(chanint,10)//进行初始化有... 查看详情
go语言学习笔记—进阶—并发编程:通道(channel)——各种各样的通道(代码片段)
单向通道在声明通道时,我们可以设置只发送或只接收。这种被约束操作方向的通道称为单向通道。声明单向通道只发送:chan<-,只接收:<-chanvar通道实例chan<-元素类型//只发送数据var通道实例<-chan元素... 查看详情