golang 的chan 信道与并发

1、select 语句

语法如下:

select {case communication clause  :statement(s);      case communication clause  :statement(s);/* 你可以定义任意数量的 case */default : /* 可选 */statement(s);
}

select 是 go 语言的一种控制结构,类似于用于通信用的 switch 语句,每一个 case 必须是一个通信操作,要么接收,要么发送

select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的

2、chan 类型

chan属于 go 语言的派生类型,用于不同的 goroutine 之间的通信

// 声明
// 1、普通声明
// 声明一个 int 型的通道
var ch chan int
ch = make(chan int)
// 2、声明后直接赋值
var ch = make(chan int)
// 2、声明一个带有缓冲的通道
// 声明一个带有一个缓冲空间的通道
var ch = make(chan int,1)

3、chan 和 select 的简单使用

3.1 chan

package mainimport "fmt"func main() {var ch = make(chan int)go func(ch chan int){ch <- 1}(ch)fmt.Println("通道 ch 传递的值为:",<- ch)
}
# 运行结果
go run main.go
通道 ch 传递的值为: 1

3.2 chan 和 select 配合使用

package mainimport "fmt"func main() {var ch = make(chan int)go func(ch chan int){ch <- 1}(ch)select {case i := <- ch:fmt.Println("通道 ch 传递的值为:",i)}
}
# 运行结果
go run main.go
通道 ch 传递的值为: 1

4、简单的实现并发

package mainimport ("fmt""time"
)func main() {var ch = make(chan string)// key = taskId,value = sleep timevar task = []int{3,2,1}startTime := time.Now()for k, v := range task {go func(taskId,sleepTime int,ch chan string) {time.Sleep(time.Second*time.Duration(sleepTime))ch <- fmt.Sprintf("当前任务为:%d,休眠时间为:%d",taskId,sleepTime)}(k,v,ch)}for range task {fmt.Println(<- ch)}endTime := time.Now()fmt.Printf("程序执行完毕,任务总耗费时间为:%v",endTime.Sub(startTime))
}
# 程序执行结果
go run main.go
当前任务为:2,休眠时间为:1
当前任务为:1,休眠时间为:2
当前任务为:0,休眠时间为:3
程序执行完毕,任务总耗费时间为:3.003920688s
# 由结果可以看出来,总计耗费时间为 3秒,结果显而易见产生了并发效果

5、通过select 控制协程超时

package mainimport ("fmt""time"
)func main() {var ch = make(chan string)// key = taskId,value = sleep timevar task = []int{3,2,1}// 超时时间设置var overtime = 2startTime := time.Now()for k, v := range task {go Run(k,v,ch,overtime)}for range task {fmt.Println(<- ch)}endTime := time.Now()fmt.Printf("程序执行完毕,任务总耗费时间为:%v",endTime.Sub(startTime))
}// 超时控制
// 再开启一个协程,执行run 方法,超时后直接 done 掉
// 使用select 造成协程内阻塞
func Run(taskId,sleepTime int,ch chan string,overTime int) {var chRun = make(chan string)go run(taskId,sleepTime,chRun)select {case re := <-chRun:ch <- recase <- time.After(time.Second * time.Duration(overTime)):ch <- fmt.Sprintf("当前任务为:%d,休眠时间为:%d,该任务已超时",taskId,sleepTime)}
}func run(taskId,sleepTime int,ch chan string) {time.Sleep(time.Second*time.Duration(sleepTime))ch <- fmt.Sprintf("当前任务为:%d,休眠时间为:%d",taskId,sleepTime)
}
# 超时时间配置为2秒,运行结果
go run main.go
当前任务为:2,休眠时间为:1
当前任务为:1,休眠时间为:2,该任务已超时
当前任务为:0,休眠时间为:3,该任务已超时
程序执行完毕,任务总耗费时间为:2.003472001s# 超时时间配置为3秒,运行结果
当前任务为:2,休眠时间为:1
当前任务为:1,休眠时间为:2
当前任务为:0,休眠时间为:3,该任务已超时
程序执行完毕,任务总耗费时间为:3.000574019s
# 通过结果可以看出,当程序运行超时后,select 阻塞,等到接收到 time.After()的通道数据,从而达到超时效果

6、以缓冲通道的方式控制协程数量

package mainimport ("fmt""time"
)func main() {// key = taskId,value = sleep timevar task = []int{3,2,1}// 注意此时也要给 通信的通道 对应的缓冲空间,不然程序会done掉var ch = make(chan string,len(task))// 超时时间设置var overtime = 3// 通过控制缓冲空间大小,控制协程数量var limitCh = make(chan bool,1)startTime := time.Now()for k, v := range task {limitCh <- truego LimitChan(k,v,ch,overtime,limitCh)}for range task {fmt.Println(<- ch)}endTime := time.Now()fmt.Printf("程序执行完毕,任务总耗费时间为:%v",endTime.Sub(startTime))
}
// 以缓冲通道的方式,限制协程数量,可以防止程序无限开启协程数量
func LimitChan(taskId,sleepTime int,ch chan string,overTime int,limitCh chan bool) {Run(taskId,sleepTime,ch ,overTime)// 任务执行完毕后,释放一个空间<- limitCh
}
// 超时控制
// 再开启一个协程,执行run 方法,超时后直接 done 掉
// 使用select 造成协程内阻塞
func Run(taskId,sleepTime int,ch chan string,overTime int) {var chRun = make(chan string)go run(taskId,sleepTime,chRun)select {case re := <-chRun:ch <- recase <- time.After(time.Second * time.Duration(overTime)):ch <- fmt.Sprintf("当前任务为:%d,休眠时间为:%d,该任务已超时",taskId,sleepTime)}
}func run(taskId,sleepTime int,ch chan string) {time.Sleep(time.Second*time.Duration(sleepTime))ch <- fmt.Sprintf("当前任务为:%d,休眠时间为:%d",taskId,sleepTime)
}
# 此时程序的执行结果为:
当前任务为:0,休眠时间为:3,该任务已超时
当前任务为:1,休眠时间为:2
当前任务为:2,休眠时间为:1
程序执行完毕,任务总耗费时间为:6.007556643s
# 当创建两个空间的缓冲通道时,结果为:
当前任务为:1,休眠时间为:2
当前任务为:0,休眠时间为:3,该任务已超时
当前任务为:2,休眠时间为:1
程序执行完毕,任务总耗费时间为:3.007228336s# 从结果分析很明显可以看到,第一次只开启了一个协程,得到的结果放到了 ch 的缓冲空间,如果没有给 ch 足够的缓冲空间,程序会 deadlock

7、需要注意的点

// 这个点当时没注意到,结果纠结了半天,然后又重新分析了一遍,才发现这个问题点
// time.After 和 time.Now().After()
// After waits for the duration to elapse and then sends the current time on the returned channel. It is equivalent to NewTimer(d).C. The underlying Timer is not recovered by the garbage collector until the timer fires. If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed
time.After(d Duration) <-chan Time
// time 包的 After 方法 返回一个 通信结构的 time
// 可以通过该方法来实习超时控制
// Example
select {
case m := <-c:     handle(m)
case <-time.After(10 * time.Second):     fmt.Println("timed out")
}

本文借鉴于https://www.jianshu.com/p/42e89de33065,有需要的可以取查看原文

golang 的chan 信道与并发相关推荐

  1. 制坯系列-Golang专题-chan

    <制坯系列-Golang专题>:chan作为协程之间通信的重要方式,是替代内存共享的最佳通信方式,本文对基本原理和关键知识点做简单介绍 chan底层数据结构 type hchan stru ...

  2. golang 学习 - chan以及chan的一下用例

    golang 学习 - chan 1. 通道 // _通道_ 是连接多个 Go 协程的管道.你可以从一个 Go 协程 // 将值发送到通道,然后在别的 Go 协程中接收.package mainimp ...

  3. golang的chan(管道)

    golang的chan翻译成中文就是管道,顾名思义,就是管道的一端用来读,另一端用来写,这与write和read函数的性质是非常相似的,比如说管道中没数据,就会发生读阻塞,管道中数据是满的,就会发生写 ...

  4. golang管道chan

    package mainimport ("fmt""time" )func main() {//test2()test4()time.Sleep(time.Se ...

  5. 如何在golang中关闭bufio.reader_Golang 并发模型系列:1. 轻松入门流水线模型

    Go语言中文网,致力于每日分享编码.开源等知识,欢迎关注我,会有意想不到的收获! Golang作为一个实用主义的编程语言,非常注重性能,在语言特性上天然支持并发,它有多种并发模型,通过流水线模型系列文 ...

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

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

  7. golang 中的信道练习 填充和遍历 备忘

    1.基础文件读取 2.切片使用 ,添加 3.随机数据使用和time时间方法使用 4.信道的使用基本知识 //channel 练习package main import("fmt"& ...

  8. Golang——TCP、UDP实现并发(服务端与客户端)

    Server端常用函数.接口: Listen函数:func Listen(network, address string) (Listener, error)network:选用的协议:TCP.UDP ...

  9. Golang make chan 第二个参数(size)

    说明 func make(t Type, size ...IntegerType) Type 指出该值在同一时刻最多可以容纳 size 个元素值. 如果我们发送给该通道的元素值未被取走,那么该通道最多 ...

  10. golang |问题代码报go并发死锁

    文章目录 背景 代码 背景 跟着老男孩第五期学go,老师的设计是使用结构体,我捣鼓能不能不用结构体.结果很多问题.做个记录. 其实主要想不明白,channel是引用类型,为什么还会有指针的channe ...

最新文章

  1. Redis 之(二) Redis的基本数据结构以及一些常用的操作
  2. C#判断Textbox是否为数字
  3. 2009年5月软件设计师考前预测试题及考点解析
  4. HTML5 Canvas save 保存恢复状态
  5. 【洛谷2624】[HNOI2008] 明明的烦恼(Python+利用prufer序列结论求解)
  6. 台式电脑计算机能创建新磁盘吗,解决方案:如何添加硬盘以扩展台式计算机上的存储空间|如何对新添加的硬盘进行分区...
  7. 区别德语的公母阴阳性别的秘诀
  8. OpenStack是吞噬金钱和人力的怪兽吗?
  9. SVN server安装步骤
  10. 如何区分独立服务器与VPS主机?
  11. uniapp打包安卓APP生成自有证书的教程,简单实用
  12. Codewars 刷题笔记(Python)6.Multiples of 3 or 5
  13. dede标签使用大全
  14. Dem与遥感影像制作三维效果简单教程
  15. autohotkey 函数
  16. matlab读Excel表格数据画图,matlab读Excel表格数据画图-matlab如何从excel表格中读取数据?...
  17. baidumap vue 判断范围_vue--百度地图点覆盖和区域划分
  18. 50天50个前端小项目(纯html+css+js)第十二天(FAQ 问题与回答框)
  19. 【AGM】《风色幻想:纷争—luca篇》角色调整版
  20. 安卓禁止root手机运行app,安卓root代码检测

热门文章

  1. ips细胞最新进展:利用iPS细胞成功培养出抑制宫颈癌繁殖的免疫杀伤T细胞,有望实现宫颈癌的免疫细胞疗法
  2. HDU 2154:跳舞毯(递推)
  3. Stronger (What Doesn't Kill You)
  4. C# 读取Word中的脚注和尾注内容
  5. 泛微OA系统新增短信宝短信插件
  6. qregexp括号匹配_qt中的正则表达式QRegExp使用大全以及匹配中括号[]方法大全
  7. iOS12系统这5个隐藏小技巧!你一定要知道,好用到停不下来!
  8. oracle其他数据对象 --- 视图(10级学员 韩晓爽课堂总结)
  9. java aspect调用,在Spring 中使用@Aspect 控制自定义注解的操作
  10. 关于:使用 OCT 自定义部署 Office 2007-2016