go语言学习-goroutine
o 语言有一个很重要的特性就是 goroutine, 我们可以使用 goroutine 结合 channel 来开发并发程序。
并发程序指的是可以同时运行多个任务的程序,这里的同时运行并不一定指的是同一时刻执行,在单核CPU的机器下,在同一时刻只可能有一个任务在执行,但是由于CPU的速度很快,在不断的切换着多个任务,让它们交替的执行,因此宏观上看起来就像是同时在运行; 而在多核的机器上,并发程序中的多个任务是可以实现在同一时刻执行多个的,此时并发的多个任务是在并行执行的。
goroutine
goroutine 是 go 语言中的并发执行单元,我们可以将多个任务分别放在多个 goroutine 中,来实现并发程序。下面先看一个例子:
package main
import "fmt"
func hello() {
fmt.Println("Hello World!!!")
}
func main() {
go hello()
fmt.Println("Bye!!!")
var input string
fmt.Scanln(&input)
}
上述程序的执行结果如下:
Bye!!!
Hello World!!!
上面这个例子展示了使用 goroutine 的几个要点:
- 程序启动时,我们的主函数 main 也是在一个单独的 goroutine 中运行的。
go hello()就是用于创建一个 goroutine, 即 go 关键字加上 要在 goroutine 中执行的函数(也可以是匿名函数,不过必须是调用的形式)- 最后两句是用于将 main 函数阻塞在这里,直到我们按下回车键,之所以这么做是因为,我们不知道新创建的 goroutine 和 main goroutine 的执行顺序,有可能主程序先执行完成,此时主程序结束,我们就看不到新 goroutine 的执行效果了。(通常不会使用这种方法)
以上就是 goroutine 的基本用法
channels
前面我们学习了怎样创建并行的执行单元,但是每个执行单元之间是完全独立的,如果我们想在运行期间交换数据,即进行通信,此时就得依靠另一个概念 - channels, 即通道,这个名字十分贴切,就像在不同的并发执行单元之间连接了一根管道,然后通过这跟管道来发送和接收数据。
goroutine 和 channel 经常结合在一起使用,下面学习一些 channel 的用法:
创建 channel
ch1 := make(chan int)
channel 也需要使用 make 函数来创建,也就是说 channel 也是一种引用类型(make函数会返回低层数据结构的引用给channel)
向 channel 中读写数据
前面说了 channel 是用于 goroutine 之间通信的, 自然能够从 channel 中写入和读取数据,使用的都是
<-操作符ch := make(chan int)
ch<- 1 // 向 channel 中写入数据
var a int = <-ch // 从 channel 中读取数据
关闭 channel
在我们使用完一个 channel 之后,可以调用 close() 方法来关闭一个 channel, 关闭之后的通道,不能够再进行数据的写操作, 但是仍然可以读取之前写入成功的数据(如果没有数据了,将返回零值)。
channel 的基本操作就是上面这么多,不过实际上,channel 是有两种的: 无缓冲的 和 有缓冲的。上面我们创建的是无缓存的,有缓存的创建方式是 ch := make(chan int, 2), 二者的区别是:
- 无缓冲的 channel 的发送操作将导致发送者的 goroutine 阻塞,直到在另一个 goroutine 上对其进行接收操作。如果先发生的是接收操作,那么接收者将被阻塞,直到在另一个 goroutine 上对其进行发送操作。
- 带缓存的 channel 可以缓存多个数据,因此不会立即阻塞,只有当缓存满了之后,发送者才可能会被阻塞,并且只有到缓存为空时,接收者才可能被阻塞
例1: 通道用于传递消息
package main
import "fmt"
func main() {
message := make(chan string) // 创建一个用于传递字符串的通道
go func() {
message <- "This is a message." // 向 channel 写入数据
}()
msg := <- message // 从 channel 读取数据
fmt.Println(msg)
}
例2: 利用通道进行同步
package main
import "fmt"
func hello() {
fmt.Println("Hello World!!!")
done <- true
}
func main() {
done := make(chan bool)
go hello()
fmt.Println("Bye!!!")
<-done // 这里会阻塞住,直到在另一个 goroutine 中对 done 进行写入操作之后
}
单向 channel
当使用 channel 作为参数,我们可以指定 channel 为单向的,即让通道在函数中只能发送,或者只能接收数据,以此来提高程序的安全性.
语法:
<-chan type表示一个只能接收数据的通道chan<- type表示一个只能发送数据的通道
例子:
package main
import "fmt"
// 这里的 message 在函数 send 中就是一个只能发送数据的通道
func send(msg string, message chan<- string) {
message<- msg
}
// 这里的 message 在函数 receive 中就是一个只能发送数据的通道
func receive(message <-chan string) string {
msg := <- message
return msg
}
func main() {
message := make(chan string)
go send("hello", message)
fmt.Println(receive(message))
}
输出结果是 hello, 此时在函数 send 中,message 通道就只能用于发送数据,而在函数 receive 中通道只能接收数据,通过参数的限制使其在函数内部成为了单向的通道。
select
go语言提供了一个 select 关键字,可以使用它来等待多个通道的操作,以实现多路复用。语法:
select {
case <-ch1:
...
case ch2 <- value:
...
default:
...
}
其中的每个 case 表示一个 channel 的操作,当case语句后面指定通道的操作可以执行时,select 才会执行 case 之后的语句。此时其他的语句都不会被执行。
例子: 超时处理
package main
import "time"
import "fmt"
func main() {
ch1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
ch1 <- "result 1"
}()
select {
case res := <- ch1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout 1")
}
ch2 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
ch2 <- "result 2"
}()
select {
case res := <-ch2:
fmt.Println(res)
case <-time.After(time.Second * 3):
fmt.Println("timeout 2")
}
}
上面的例子中我们定义了两个通道和两个select结构,是为了进行对比,第一个channel会在等待两秒之后被写入数据,而在 select 中,第二个case语句只会等待一秒,然后就会执行,因此就会执行超时操作。而在第二个 select 中,第二个 case 语句会等待三秒。所以上述程序的结果如下:
timeout 1
result 2
go语言学习-goroutine的更多相关文章
- 浅谈Go语言的Goroutine和协程
0x00.前言 前面写了一篇初识Go语言和大家一起学习了Go语言的巨大潜力.语言简史.杀手锏特性等,感兴趣的读者可以回顾一下. 今天来学习Go语言的Goroutine机制,这也可能是Go语言最为吸引人 ...
- Go语言学习笔记(1)——顺序编程
Go语言学习笔记这一堆主要是<Go语言编程>(人民邮电出版社)的读书笔记.中间会穿插一些零碎的点,比如源码学习之类的.大概就是这样吧. 1. 顺序编程 1.1 变量 变量的声明: var ...
- go语言学习笔记-初识Go语言
Go语言是怎样诞生的? Go语言的创始人有三位,分别是图灵奖获得者.C语法联合发明人.Unix之父肯·汤普森(Ken Thompson).Plan 9操作系统领导者.UTF-8编码的最初设计者罗伯·派 ...
- C语言学习 第八次作业总结
本次作业其实没有新的内容,主要就是复习上一次的一维数组的相关内容.冯老师布置了5道题目,其中涉及到一些比较简单的排序或者是查找的方法.因为数据很少,所以直接使用for循环遍历就可以了. 关于本次作业, ...
- C语言学习 第七次作业总结
C语言学习 第七次作业总结 数组可以分为数组和多下标数组(在传统的国内C语言书本中,将其称为二/多维数组). 数组名称 在之前的课程中,大家应该都有印象,对于int a这样的定义,会为变量 a 声明一 ...
- 技能收获与C语言学习
你有什么技能比大多人(超过90%以上)更好? 我会的东西很多,喜欢的东西太多,但是很遗憾广而不专,会而不精.学了很多东西我都是为了娱乐,因为以前我们那里过于强调学习,很多爱好也都被扼杀在摇篮里.我觉得 ...
- 一份关于Swift语言学习资源的整理文件
一份关于Swift语言学习资源的整理文件 周银辉 在这里下载 https://github.com/ipader/SwiftGuide
- go语言学习笔记
Go语言学习基本类型Bool 取值范围:true,false (不可以用数字代替)Int/uint 根据平台可能为32或64位int8/uint8 长度:1字节 取值范围-128~127/0~255b ...
- 20155206赵飞技能获取经验,C语言学习感想与对JAVA的学习目标
自己较强的技能获取经验. 1:实话实说我自己是没有哪个技能可以超过90%的人的,只有自认为做的还可以的一些事情,例如打篮球,office软件的应用,一百米跑.至于其他方面就是很平庸了. 2:经验主要有 ...
随机推荐
- iOS视频流开发(2)—视频播放
承上篇,本篇文章主要介绍iOS视频播放需要用到的类.以及他们的使用场景和开发中遇到的问题. MPMoviePlayerViewController MP简介 iOS提供MPMoviePlayerCon ...
- XMLHttpRequest: 网络错误 0x2f78,…00002f78
常在河边走,怎能不湿脚,在web前端开发的过程中总是遇到很多关于IE的故事. 一个get请求,传了一个json对象,包含一串参数,在IE上就出现了这个问题:XMLHttpRequest: 网络错误 0 ...
- 论文笔记系列-DARTS: Differentiable Architecture Search
Summary 我的理解就是原本节点和节点之间操作是离散的,因为就是从若干个操作中选择某一个,而作者试图使用softmax和relaxation(松弛化)将操作连续化,所以模型结构搜索的任务就转变成了 ...
- redis实现消息队列&发布/订阅模式使用
在项目中用到了redis作为缓存,再学习了ActiveMq之后想着用redis实现简单的消息队列,下面做记录. Redis的列表类型键可以用来实现队列,并且支持阻塞式读取,可以很容易的实现一个高性 ...
- spring整合ehcache2.5.2缓存异常-- net.sf.ehcache.CacheException
报错如下: The source of the existing CacheManager is: DefaultConfigurationSource [ ehcache.xml or ehcach ...
- vlc-android 的编译过程
参考官方文档:https://wiki.videolan.org/AndroidCompile#Get_VLC_Source 值得注意的的地方: 1.切记安装以下工具 sudo apt-get ins ...
- win7下出现读不到移动硬盘的解决办法
很多电脑会出现移动硬盘读不到,或者 读到部分盘的情况,那么下面我就为大家来一一解决这些情况: 方法一: 最常见的是硬盘供电不足导致 ,要么换一个硬盘盒子,要么给硬盘一个外加电源即可方法二: ...
- headers 替换脚本
python代码 headers = """ Accept: */* Accept-Encoding: gzip, deflate, br Accept-Language ...
- java并发编程系列一、多线程
一.什么是线程 一个应用就是一个进程.一个进程由多个线程组成.一个生产车间比作是一个进程.工人比作是线程.当任务比较多的时候,增加工人可以提高效率,同时成本就是支付费用(机器资源,内存)也会增加. p ...
- VMware如何给虚拟机添加新硬盘
在使用虚拟机时,若硬盘空间不足.或需要用到双硬盘或多硬盘环境时,我们可以给虚拟机新增虚拟硬盘