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 ...
随机推荐
- Python 爬虫工程师必看,深入解读字体反爬虫
字体反爬虫开篇概述 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人 ...
- Alink漫谈(十七) :Word2Vec源码分析 之 迭代训练
Alink漫谈(十七) :Word2Vec源码分析 之 迭代训练 目录 Alink漫谈(十七) :Word2Vec源码分析 之 迭代训练 0x00 摘要 0x01 前文回顾 1.1 上文总体流程图 1 ...
- JavaScript map+parseInt 容易产生的误区
map /** * 语法: * var new_array = arr.map(function callback(currentValue[,index[,array]]){ * // return ...
- LeetCode 392. Is Subsequence 详解
题目详情 给定字符串 s 和 t ,判断 s 是否为 t 的子序列. 你可以认为 s 和 t 中仅包含英文小写字母.字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 & ...
- LeetCode 115.不同的子序列 详解
题目详情 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数. 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串.(例如, ...
- 使用halo搭建自己的博客并配置https域名访问
首先进行java配置 # 1. 下载jdk [下载地址](https://www.oracle.com/cn/java/technologies/javase-downloads.html) - 一定 ...
- 如何将返回的JSon字符串用MAP格式读取
语法是这样: ObjectMapper mapper = new ObjectMapper(); Map resultMap=null; resultMap = mapper.readValue(in ...
- Java继承后访问成员的特点
继承后的特点--成员变量 对象访问成员变量时,会先在子类中查找有没有定义对应的变量,若子类中存在就会就近使用子类中的变量,若子类中没有定义就会沿着继承关系往上找有没有定义相应的变量,若父类中也没有则编 ...
- 创建SpringMVC项目
学习SpringMVC框架第一步,先创建一个简单项目,了解流程.使用的是Eclipse+Tomcat9.0 创建项目springmvc 新建Dynamic Web Project File->N ...
- Java之Annotation(注解)——注解处理器
如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了.使用注解的过程中,很重要的一部分就是创建于使用注解处理器.Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处 ...