Go chan基础1
值表示
通道类型是一个引用类型,所以初始化前,为此类型的零值:nil。
初始化通道
缓冲通道(10):
make(chan int,10)
make(chan int)
非缓冲通道:
make(chan int)
一个通道值的缓冲容量总是固定不变的。如果第二个参数值被省略了,就表示被初始化的这个通道永远无法缓冲任何元素值。发送给它的元素值应该被立刻取走,否则发送方的goroutine就会被暂停(或者说阻塞),直到有接收方接收这个元素值。
需要特别注意:
试图从一个未被初始化的通道值(即值为nil的通道)那里接收值,会造成当前goroutine的永久阻塞!
同样,向nil 通道 发送值,goroutine也会被永久阻塞!
操作特性
通道是在多个goroutine之间传递数据和同步数据的重要手段,而对通道的操作本身也是同步的。
在同一时刻,仅有一个goroutine能向一个通道发送值,
同时也仅有一个goroutine能接收值。
在通道中,先进先出,相当于FIFO的消息队列。
通道中的每个值都只可能被某一个goroutine接收,已接受的值会立刻从通道中删除。
Go语言中 空结构类型的变量是不占用任何内存空间的,
并且所有该类型的变量都拥有相同的内存地址。
建议用于传递"信号"的通道,都以struct{}作为元素类型,除非需要传递更多信息。
package mainimport ("fmt""time"
)var strChan = make(chan string, 3)func main() {// 通知接受操作chansyncChan1 := make(chan struct{}, 1)// 避免主goroutine过早结束syncChan2 := make(chan struct{}, 2)go func() { // 用于接收操作。<-syncChan1fmt.Println("Received a sync signal and wait a second... [receiver]")time.Sleep(time.Second)for {if elem, ok := <-strChan; ok {fmt.Println("Received:", elem, "[receiver]")} else {break}}fmt.Println("Stopped. [receiver]")syncChan2 <- struct{}{}}()go func() { // 用于发送操作。for _, elem := range []string{"a", "b", "c", "d"} {strChan <- elemfmt.Println("Sent:", elem, "[sender]")if elem == "c" {syncChan1 <- struct{}{}fmt.Println("Sent a sync signal. [sender]")}}fmt.Println("Wait 2 seconds... [sender]")time.Sleep(time.Second * 2)close(strChan)syncChan2 <- struct{}{}}()<-syncChan2<-syncChan2
}
//输出
Sent: a [sender]
Sent: b [sender]
Sent: c [sender]
Sent a sync signal. [sender]
Received a sync signal and wait a second... [receiver]
Received: a [receiver]
Received: b [receiver]
Received: c [receiver]
Received: d [receiver]
Sent: d [sender]
Wait 2 seconds... [sender]
Stopped. [receiver]
除此之外,如果有多个goroutine因向同一个已满的通道发送元素值而被阻塞,那么当该通道中有多余空间的时候,最早被阻塞的那个goroutine会最先被唤醒。对于接收操作来说,也是如此,一旦已空的通道中有了新的元素值,那么最早因从该通道接收元素值而被阻塞的那个goroutine会最先被唤醒。
并且, Go运行时系统每次只会唤醒一个goroutine。
还有一点需要注意:发送方向通道发送的值会被复制,接收方接收的总是该值的副本,而不是该值本身。经由通道传递的值至少会被复制一次,至多会被复制两次。
例如,当向一个已空的通道发送值,且已有至少一个接收方因此等待时,该通道会绕过本身的缓冲队列,直接把这个值复制给最早等待的那个接收方。
又例如,当从一个已满的通道接收值,且已有至少一个发送方因此等待时,该通道会把缓冲队列中最早进入的那个值复制给接收方,再把最早等待的发送方要发送的数据复制到那个值的原先位置上。通道的缓冲队列属于环形队列,所以这样做是没问题的。
这种情况下,涉及的那两个值在传递到接收方之前都会被复制两次。
因此,当接收方从通道接收到一个值类型的值时,对该值的修改就不会影响到发送方持有的那个源值。但对于引用类型的值来说,这种修改会同时影响收发双方持有的值。
// chanval1.go
package mainimport ("fmt""time"
)var mapChan = make(chan map[string]int, 1)func main() {syncChan := make(chan struct{}, 2)go func() { // 用于演示接收操作。for {if elem, ok := <-mapChan; ok {elem["count"]++} else {break}}fmt.Println("Stopped. [receiver]")syncChan <- struct{}{}}()go func() { // 用于演示发送操作。countMap := make(map[string]int)for i := 0; i < 5; i++ {mapChan <- countMaptime.Sleep(time.Millisecond)fmt.Printf("The count map: %v. [sender]\n", countMap)}close(mapChan)syncChan <- struct{}{}}()<-syncChan<-syncChan
}
//输出
The count map: map[count:1]. [sender]
The count map: map[count:2]. [sender]
The count map: map[count:3]. [sender]
The count map: map[count:4]. [sender]
The count map: map[count:5]. [sender]
Stopped. [receiver]
chan val 值类型
// chanval2.go
package mainimport ("fmt""time"
)// Counter 代表计数器的类型。
type Counter struct {count int
}// 引用类型,其值类型属性不会被修改
var mapChan = make(chan map[string]Counter, 1)
//var mapChan = make(chan map[string]*Counter, 1)func main() {syncChan := make(chan struct{}, 2)go func() { // 用于演示接收操作。for {if elem, ok := <-mapChan; ok {counter := elem["count"]counter.count++} else {break}}fmt.Println("Stopped. [receiver]")syncChan <- struct{}{}}()go func() { // 用于演示发送操作。countMap := map[string]Counter{"count": Counter{},}//countMap := map[string]*Counter{// "count": &Counter{},//}for i := 0; i < 5; i++ {mapChan <- countMaptime.Sleep(time.Millisecond)fmt.Printf("The count map: %v. [sender]\n", countMap)}close(mapChan)syncChan <- struct{}{}}()<-syncChan<-syncChan
}
//输出
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
The count map: map[count:{0}]. [sender]
Stopped. [receiver]
chan val 引用类型
package mainimport ("fmt""time"
)// Counter 代表计数器的类型。
type Counter struct {count int
}//var mapChan = make(chan map[string]Counter, 1)
// 指针引用类型的指针类型,其值类型属性 会被修改
var mapChan = make(chan map[string]*Counter, 1)func main() {syncChan := make(chan struct{}, 2)go func() { // 用于演示接收操作。for {if elem, ok := <-mapChan; ok {counter := elem["count"]counter.count++} else {break}}fmt.Println("Stopped. [receiver]")syncChan <- struct{}{}}()go func() { // 用于演示发送操作。//countMap := map[string]Counter{// "count": Counter{},//}countMap := map[string]*Counter{"count": &Counter{},}for i := 0; i < 5; i++ {mapChan <- countMaptime.Sleep(time.Millisecond)fmt.Printf("The count map: %v. [sender]\n", countMap)}close(mapChan)syncChan <- struct{}{}}()<-syncChan<-syncChan
}
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
The count map: map[count:0xc00001a078]. [sender]
Stopped. [receiver]
关闭通道
package mainimport "fmt"func main() {dataChan := make(chan int, 5)syncChan1 := make(chan struct{}, 1)syncChan2 := make(chan struct{}, 2)go func() { // 用于演示接收操作。<-syncChan1for {if elem, ok := <-dataChan; ok {fmt.Printf("Received: %d [receiver]\n", elem)} else {break}}fmt.Println("Done. [receiver]")syncChan2 <- struct{}{}}()go func() { // 用于演示发送操作。for i := 0; i < 5; i++ {dataChan <- ifmt.Printf("Sent: %d [sender]\n", i)}close(dataChan)syncChan1 <- struct{}{}fmt.Println("Done. [sender]")syncChan2 <- struct{}{}}()<-syncChan2<-syncChan2
}
Go chan基础1相关推荐
- golang基础-chan的select操作、定时器操作、超时控制、goroutine中使用recover
chan的只读和只写 a.只读chan的声明 Var 变量的名字 <-chan int Var readChan <- chan int b. 只写chan的声明 Var 变量的名字 ch ...
- 记录零基础GO编程入门笔记之一
目录 一.去安装和配置 1.1写我的第一个去程序 二.Go 开发利器:VSCode 2.1为什么选择VSCode? 2.2下载安装 2.3将code命令添加到系统PATH中 2.4安装 Go 插件 2 ...
- Golang 笔记 1 基础、基本数据类型
一.Go语言基础 1. 基础 Go语言中的标识符必须以字母(Unicode字母,PHP/JS可以用中文作为变量名)下划线开头.大写字母跟小写字母是不同的:Hello和hello是两个不同的名字. ...
- gRPC 的 4 种基础通信模式
题图 | from freepik 本文将讨论 gRPC 应用程序的 4 种基础通信模式:一元 RPC.服务器端流 RPC.客户端流 RPC 以及双向流 RPC.在这个过程中,我们会使用一些真实用例 ...
- 周志华教授专著《集成学习:基础与算法》上市,破解AI实践难题
[ 摘要 ]<集成学习:基础与算法>上市一周,斩获京东IT新书销量榜第一名桂冠,并拿下京东IT图书销量总榜第二名的惊人成绩. 文中有数据派独家福利哦 本书共读活动已正式开启,文末加入读者交 ...
- 零基础学python免费网课-零基础学Python量化投资,超值线上课程反复回看
原标题:零基础学Python量化投资,超值线上课程反复回看 超值网络课程 量化投资是一种严谨.系统化的投资方式,相比起传统投资,量化投资风险低回报高,但是它要求投资者使用数据处理分析.计算机编程技术. ...
- 计算机视觉-计算机视觉开源库OpenCV基础
1.加载.显示.保存图像 import argparse import cv2ap = argparse.ArgumentParser() ap.add_argument("-i" ...
- 鸟哥的Linux私房菜(基础篇)- 第二十四章、 X Window 配置介绍
第二十四章. X Window 配置介绍 最近升级日期:2009/08/07 在 Linux 上头的图形介面我们称之为 X Window System,简称为 X 或 X11 罗!为何称之为系统呢?这 ...
- Nutanix:将IT基础架构“隐形”,让云更简单
Nutanix以超融合被全球用户熟知,作为超融合的先驱者,该公司凭借着领先的技术以及在产品与服务上不断的精益求精,获得了许多企业客户的认可.虽然"超融合"标签显眼,但在云的大趋势下 ...
最新文章
- python scapy 函数_【python|scapy】sprintf输出时raw_string转string
- Spring实战3-Spring之旅
- uni app 调用网络打印机_前端工程师 | 原生小程序坑点:uni-app到底好用在哪里?...
- 图像局部显著性—点特征(GLOH)
- OA(part2)--Outlier Evaluation Techniques
- Java中的ThreadPoolExecutor类
- c 结构体之位域(位段)
- ie8不支持console.log()的解决方法
- SQL Server 两个时间段的差and时间截取到时分
- 0ctf-2017-babyheap图解
- 后台弹出界面权限踩坑
- 懒人起名神器,百度翻译内容改为驼峰格式
- 关于yd ui 移动端自适应的方案
- 【VUE项目实战】64、CND优化ElementUI以及首页内容定制
- 即便到愚人节,也千万别做的恶作剧!
- Mac 开机密码忘记
- 计算机网络知识总结及知识网图
- 开源节流debouncethrottle
- 17. 监测 web 网站的可用性
- 超低功耗寝室指纹锁(电池供电)