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:经验主要有 ...
随机推荐
- group by与avg(),max(),min(),sum()函数的关系
数据库表: create table pay_report( rdate varchar(8), --日期 region_id varchar(4), --地市 ...
- mysql 索引无法使用问题
今天碰到一个问题,表中有一个索引不使用,怎么强制也没用 ,force index都没用, 后来才发现是类型不对, 比如索引字段是int,如果参数使用varchar,那么是无法使用索引的,参数类型最好统 ...
- ftpdata目录下日期目录权限问题
由于APP Server由root用户启动,创建目录默认为root:root用户:用户组权限 需chown -R wingupload ftpdata执行后,WING才能上传成功 或者写成脚本,每天凌 ...
- log4j2使用入门(二)——与不同日志框架的适配
在之前博客中已经指出log4j2可以与不同的日志框架进行适配,这里举一些实际应用进行说明: 1.比如我们在项目中使用了log4j2作为日志器,使用了log4j-api2.6.2.jar和log4j-c ...
- CentOS6.5运行yum报错:No module named yum
公司测试机环境不知道给我卸了什么包,导致yum运行报错状况: 报错情况: There was a problem importing one of the Python modulesrequired ...
- redhat换用centos源
解除原有源rpm -aq|grep yum|xargs rpm -e --nodepsrpm -aq|grep python-iniparse|xargs rpm -e --nodeps rpm -q ...
- Java中日期格式化SimpleDateFormat类包含时区的处理方法
1.前言 需要把格式为“2017-02-23T08:04:02+01:00”转化成”23-02-2017-T15:04:02“格式(中国时区为+08:00所以是15点),通过网上查找答案,发现没有我需 ...
- javascript 练习题目答案
//廖雪峰的博客的这个教程的答案//https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb300 ...
- Android TimeAnimator
TimeAnimator:提供了一个简单的回调机制,通过 TimeAnimator.TimeListener,在动画的每一帧处通知你.这个动画器没有时间,插值或是对象值设定.回调监听器为每一帧动画接受 ...
- 【splunk】用正则表达式提取字段
设input输入数据为 http://192.168.23.121/xxx 想提取出里面的ip,可以用rex source="xxx.csv" |rex field=input ...