值表示
通道类型是一个引用类型,所以初始化前,为此类型的零值: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相关推荐

  1. golang基础-chan的select操作、定时器操作、超时控制、goroutine中使用recover

    chan的只读和只写 a.只读chan的声明 Var 变量的名字 <-chan int Var readChan <- chan int b. 只写chan的声明 Var 变量的名字 ch ...

  2. 记录零基础GO编程入门笔记之一

    目录 一.去安装和配置 1.1写我的第一个去程序 二.Go 开发利器:VSCode 2.1为什么选择VSCode? 2.2下载安装 2.3将code命令添加到系统PATH中 2.4安装 Go 插件 2 ...

  3. Golang 笔记 1 基础、基本数据类型

    一.Go语言基础 1. 基础 Go语言中的标识符必须以字母(Unicode字母,PHP/JS可以用中文作为变量名)下划线开头.大写字母跟小写字母是不同的:Hello和hello是两个不同的名字.    ...

  4. gRPC 的 4 种基础通信模式

    题图 |  from freepik 本文将讨论 gRPC 应用程序的 4 种基础通信模式:一元 RPC.服务器端流 RPC.客户端流 RPC 以及双向流 RPC.在这个过程中,我们会使用一些真实用例 ...

  5. 周志华教授专著《集成学习:基础与算法》上市,破解AI实践难题

    [ 摘要 ]<集成学习:基础与算法>上市一周,斩获京东IT新书销量榜第一名桂冠,并拿下京东IT图书销量总榜第二名的惊人成绩. 文中有数据派独家福利哦 本书共读活动已正式开启,文末加入读者交 ...

  6. 零基础学python免费网课-零基础学Python量化投资,超值线上课程反复回看

    原标题:零基础学Python量化投资,超值线上课程反复回看 超值网络课程 量化投资是一种严谨.系统化的投资方式,相比起传统投资,量化投资风险低回报高,但是它要求投资者使用数据处理分析.计算机编程技术. ...

  7. 计算机视觉-计算机视觉开源库OpenCV基础

    1.加载.显示.保存图像 import argparse import cv2ap = argparse.ArgumentParser() ap.add_argument("-i" ...

  8. 鸟哥的Linux私房菜(基础篇)- 第二十四章、 X Window 配置介绍

    第二十四章. X Window 配置介绍 最近升级日期:2009/08/07 在 Linux 上头的图形介面我们称之为 X Window System,简称为 X 或 X11 罗!为何称之为系统呢?这 ...

  9. Nutanix:将IT基础架构“隐形”,让云更简单

    Nutanix以超融合被全球用户熟知,作为超融合的先驱者,该公司凭借着领先的技术以及在产品与服务上不断的精益求精,获得了许多企业客户的认可.虽然"超融合"标签显眼,但在云的大趋势下 ...

最新文章

  1. python scapy 函数_【python|scapy】sprintf输出时raw_string转string
  2. Spring实战3-Spring之旅
  3. uni app 调用网络打印机_前端工程师 | 原生小程序坑点:uni-app到底好用在哪里?...
  4. 图像局部显著性—点特征(GLOH)
  5. OA(part2)--Outlier Evaluation Techniques
  6. Java中的ThreadPoolExecutor类
  7. c 结构体之位域(位段)
  8. ie8不支持console.log()的解决方法
  9. SQL Server 两个时间段的差and时间截取到时分
  10. 0ctf-2017-babyheap图解
  11. 后台弹出界面权限踩坑
  12. 懒人起名神器,百度翻译内容改为驼峰格式
  13. 关于yd ui 移动端自适应的方案
  14. 【VUE项目实战】64、CND优化ElementUI以及首页内容定制
  15. 即便到愚人节,也千万别做的恶作剧!
  16. Mac 开机密码忘记
  17. 计算机网络知识总结及知识网图
  18. 开源节流debouncethrottle
  19. 17. 监测 web 网站的可用性
  20. 超低功耗寝室指纹锁(电池供电)

热门文章

  1. 零跑C11斩获大奖,带来了极致的挑战
  2. 办公室设计公司关于办公室装修全过程解剖
  3. 无线充电宝CE认证和FCC认证怎么办理?无线充移动电源UL报告
  4. 计算锋生的函数 frontogenesis
  5. Unity游戏配置存储方案
  6. android 5.1 flash,Flash Player for Android 4.0 and 5.1 以上版本(提供下載)
  7. W-Hive 支持苹果 iOS16 新版本
  8. 小米2S手机开启开发者选项
  9. 学习设计到底应该学习figma、sketch还是PS
  10. 手机端网页技术--使自己做的asp.net网页适应手机浏览