Go语言 | goroutine不只有基础的用法,还有这些你不知道的操作
今天是golang专题第15篇文章,我们来继续聊聊channel的使用。
在我们的上篇文章当中我们简单介绍了golang当中channel的使用方法,channel是golang当中一个非常重要的设计,可以理解为生产消费者模式当中的队列。但channel和队列不一样的是,golang当中集成了一些其他的用法,使得我们的使用更加灵活,开发并发相关的功能更加简单。
select机制
我们来思考一个问题,假设我们的数据源有多个,也就是说我们可能会从多个入口获取数据,但是我们并不知道这些数据源当中哪个先把数据准备好。我们希望实现轮询这些channel,哪个数据准备好了就读取哪个,否则就阻塞等待,这个功能应该怎么办呢?
我们当然可以自己用循环来实现,但是这显然是不合理的,golang当中针对这个问题提供了专门的解决方法,它就是select关键字。
select机制并不是golang这门语言独创的,早在Unix时代就有了select机制。通过select函数监控一系列文件的句柄,一旦其中一个发生了改动,select函数就会返回。而golang当中的select则用来在channel当中进行选择,有点像是switch,写出来的代码大概是这个样子:
select {
case <- chan1:
case chan2 <- 1:
default:
}
select后面跟多个case以及default,其中default并不是必须的。case后面必须要接一个chan的操作,可以是从一个chan当中读入数据,也可以是向一个chan当中写入数据。如果所有的case都不成功,则会进入default语句当中,如果没有default语句,那么select会陷入阻塞。
一般情况下我们使用select是为了从多个数据源中获取数据,当多个chan同时有数据的时候,使用select可以让我们避免判断哪个数据源数据ready的问题。
range机制
我们之前在介绍slice遍历的时候曾经介绍过range机制,我们可以通过range来遍历一个数组或者是map。就像是这样:
arr := make([]int, 0)
for i := range arr {
// do something
}
mp := make(map[string]int)
for k, v := range mp {
// do something
}
很多时候我们会把这个用法当做是迭代器的迭代,就像是Java和Python中的那样。但实际上range机制的底层原理是chan,当我们使用range的时候,它表示会不断地从chan当中接受值,直到它关闭。
所以我们也可以这样遍历一个chan当中的数据:
ch := make(chan int)
for c := range ch {
// do something
}
超时机制
有没有想过一个问题,channel的写入和写出都是阻塞的,也就是说如果是从chan当中读取数据,必须要上游已经传输了才可以读取到。同样,如果往没有缓冲区的chan写入数据也需要下游消费了才能写入成功。阻塞往往是有很大隐患的,如果处理不好很容易导致整个程序锁死。
我们需要设计机制来解决这个问题,比较好的方案就是设置定时器,如果超过一定的时间chan还没有响应成功的话,那么就人工停止程序。这一点说起来还有点麻烦的,比如我们要启动一个定时器,要手动终止goroutine,但是结合select机制其实并不难实现,我们来看代码。
timeout := make(chan bool)
go func() {
time.Sleep(1e9)
timeout <- true
}()
select {
case <- ch:
// do something
case <- timeout:
// break
}
说白了很简单,也就是我们额外启动一个goroutine做休眠操作,当休眠结束之后也通过chan发送消息,这样如果我们select先接受到了timeout的信号就说明了程序已经超时了。当然这只是一个很简单的demo,实际使用的话需要考虑的情况可能还会更多。
channel传递
有没有想过一个问题,既然chan可以传输任何类型的数据,那么我们能不能用一个chan传输一个chan呢?
这样的操作是可以的,因为在有些场景当中相比于直接把数据传输给下游,我们传输读取数据的chan可能更加方便。有点授人以渔的意思,更加厉害的是我们可以结合函数式编程,把处理数据的函数一并传输给下游。这样下游读取到数据,并且用读取到的处理函数来处理,这样可以更加定制化,如果以后数据和处理方式都发生改动,也只需要在上游修改,可以更加解耦。
我们同样来看一个demo:
type MetaData struct {
value interface{}
handler func(interface{}) int
downstream chan interface{}
}
func handle(queue chan *MetaData) {
for data := range queue {
data.downstream <- data.handler(data.value)
}
}
这只是一个简单的案例,想要在实际应用当中真的使用上还需要定义大量的接口以及做很多设计。我们只需要知道有这么一种设计模式和用法就可以了。
单向channel
最后,我们来说说单向channel,也就是说我们指定channel是只读的或者是只写的。但其实这是一个伪命题,原因也很简单,如果只写数据没人读,或者是只读但是不能写,那么这个channel有什么用呢?只有有人读有人写才可以完成数据流通不是吗?
的确如此,所以这里所说的单向channel其实并不是真正意义上的单向,只是说我们为了规范,对使用方进行限制。比如说我们限定在消费函数当中不能写入,在生产函数当中不能消费。我们在通过函数传递chan的时候,可以通过加上限定让chan在函数当中变成单向的。
var ch chan <- float32 // 只写chan
var ch <- chan float32 // 只读chan
除此以外我们还可以把一个正常的chan转化成单向的chan:
var ch chan int
ch1 := <- chan int(ch)
ch2 := chan <- int(ch)
我们一般不在程序当中做这样的转化,而是用在函数参数当中,这也主要是为了起到规范的作用。
func Test(ch <- chan int) {
for val := range ch {
// do something
}
}
今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。
- END -
{{uploading-image-498941.png(uploading...)}}
Go语言 | goroutine不只有基础的用法,还有这些你不知道的操作的更多相关文章
- Go语言 - goroutine
并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天). 并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天). Go语言的并发通过goroutine实现.goroutin ...
- 《C#语言和数据库技术基础》单词必备
<C#语言和数据库技术基础> 第一章1..NET Framework 框架2.sharp 尖锐,强烈的3.application 应用程序4.devel ...
- Go语言 异常panic和恢复recover用法
Go语言 异常panic和恢复recover用法 背景:Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在 ...
- TODO:Go语言goroutine和channel使用
TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...
- 《OOC》笔记(3)——C语言变长参数va_list的用法
<OOC>笔记(3)——C语言变长参数va_list的用法 C语言中赫赫有名的printf函数,能够接受的参数数目不固定,这就是变长参数.C#里也有params这个关键字用来实现变长参数. ...
- C语言中关于scanf函数的用法
scanf()函数的控制串 函数名: scanf 功 能: 执行格式化输入 用 法: int scanf(char *format[,argument,...]); scanf()函数是通用终端格式化 ...
- 快看Sample代码,速学Swift语言(2)-基础介绍 快看Sample代码,速学Swift语言(1)-语法速览
快看Sample代码,速学Swift语言(2)-基础介绍 Swift语言是一个新的编程语言,用于iOS, macOS, watchOS, 和 tvOS的开发,不过Swift很多部分内容,我们可以从C或 ...
- C语言的10大基础算法
C语言的10大基础算法 算法是一个程序和软件的灵魂,作为一名优秀的程序员,只有对一些基础的算法有着全面的掌握,才会在设计程序和编写代码的过程中显得得心应手.本文包括了经典的Fibonacci数列.简易 ...
- go语言goroutine
Go语言goroutine 在别的语言里想要在一个程序中实现多任务,如python,python实现多任务可以使用多进程.多线程.携程.但多进程占用资源,多线程无法发挥多核的优势(GIL),pytho ...
随机推荐
- 使用docker快速搭建hive环境
记录一下使用docker快速搭建部署hive环境 目录 写在前面 步骤 安装docker 安装docker 安装docker-compose 配置docker国内镜像源(可选) 安装git & ...
- pyinstaller 转 exe 的一些坑,以及解决
2020年7月30日 打包了一个程序,各种报错,记录一下1.有时候,这个pyinstaller 打包程序吧,会出现闪退一下,一般原因是因为某个包没有加载进来,或者是包所在的路劲下面有中文,这样打包成功 ...
- JDBC的开发步骤
一.JDBC概述 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问, 它由一组用Jav ...
- 2020-06-07:mysql中varchar类型的id,where id=1,会用到索引吗?int 类型的id,where id="1",会用到索引吗?为什么?
福哥答案2020-06-07: 答案来自群员:对于int类型id,查询的varchar 类型 ‘1’会隐式转换成 1,‘1’和 1都能正常走索引:对于varchar类型id,查询的int 类型 1不会 ...
- 解读闭包,这次从ECMAScript词法环境,执行上下文说起
对于x年经验的前端仔来说,项目也做了好些个了,各个场景也接触过一些.但是假设真的要跟面试官敞开来撕原理,还是有点慌的.看到很多大神都在手撕各种框架原理还是有点羡慕他们的技术实力,羡慕不如行动,先踏踏实 ...
- 详解POW工作量证明原理
原文地址 来自 微信公众号 区块链大师 POW工作量证明(英文全称为Proof of Work)早在比特币出现之前就已经有人探索,常见的是利用HASH运算的复杂度进行CPU运算实现工作量确定,当然你 ...
- moonlight不显示鼠标指针
多显示屏导致moonlight不显示鼠标指针, 使用的时候关闭其他显示屏,只使用一个显示屏,就可以正常显示了.
- PYTHON-错误-函数有返回值未接收导致替换不成功
#1.有返回值,没有赋值,替换不成功 cxj = 'guapi' cxj.replace(cxj,'2b') print(cxj) #2.有返回值,赋值,替换成功 cxj = 'guapi' cxj ...
- 面试现场:裸面京东JavaT5第一轮电话面试
人物简介 清辞女朋友,94年资深女程序员一枚,15年非本专业毕业,有过1年测试经验,3年Java开发经验,之前有个1年大公司的工作经历,2年的车联网公司经历,每天打卡式工作,朝九晚七.工作之余没看过一 ...
- python 09 数据包 异常处理
pickle模块操作文件 pickle.dump(obj, file[, protocol]) 序列化对象,并将结果数据流写入到文件对象中.参数protocol是序列化模式,默认值为0,表示以文本的形 ...