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:经验主要有 ...
随机推荐
- BIM 相关资料
BIM 相关资料:http://pan.baidu.com/share/link?shareid=919219072&uk=966240844
- 【译】.NET Core 2.2 Preview 2 发布
原文出自.Net Blog Announcing .NET Core 2.2 Preview 2 今天,我们宣布推出.NET Core 2.2 Preview 2.我们有很多重要改进要和你分享,而且我 ...
- Win10 + VS2017 15.5.6 环境下解决 Python 3.6 环境无法刷新DB的问题
作为宇宙第一IDE,VS2017对Python的支持还算可以,虽然和PyCharm等Python专用IDE相比还有些差距,但是经过后续的更新升级,我相信VS2017将越来越完善.由于本人一直都是使用V ...
- 【bzoj2653】【middle】【主席树+二分答案】
Description 一个长度为 n 的序列 a ,设其排过序之后为 b ,其中位数定义为 b[n/2] ,其中 a,b 从 0 开始标号 , 除法取下整. 给你一个长度为 n 的序列 s .回答 ...
- sublime汉化步骤记录
1.下载sublime编辑器,下载地址:http://www.sublimetext.com/ 2.安装sublime 3.首先安装“Package Control”(如果已经安装过可以跳过此步骤) ...
- 转载:《理解OAuth 2.0》 阮一峰
原文:http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛 ...
- windows安装配置git和Tortoisegit
git github gitlab Tortoisegit 的概念自行百度 1. 安装git 2. 安装小乌龟:Tortoisegit 和中文包 3. 配置 4. 使用 参考: 目录 安装及配置 ...
- Python-GIL 进程池 线程池
5.GIL vs 互斥锁(*****) 1.什么是GIL(Global Interpreter Lock) GIL是全局解释器锁,是加到解释器身上的,保护的就是解释器级别的数据 (比如垃圾回收的数据) ...
- webpack文件笔记
webpack.prod.conf.js里面的ExtractTextPlugin,把css文件提取出来,专门进行打包minify :压缩 依赖的第三方库打包到vendor.js里面 每次项目打包的时候 ...
- JUnit单元测试--IntelliJ IDEA
单元测试的基本使用 一.环境配置 使用idea IDE 进行单元测试,首先需要安装JUnit 插件. 1.安装JUnit插件步骤 File-->settings-->Plguins--&g ...