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:经验主要有 ...
随机推荐
- delphi-TTcpServer与TTcpClient
最简单的TTcpServer与TTcpClient通信实例-Delphi_海盗船长_新浪博客http://blog.sina.com.cn/s/blog_5383794d0100nt9u.html d ...
- 数据库操作之整合Mybaties和事务讲解 5节课
1.SpringBoot2.x持久化数据方式介绍 简介:介绍近几年常用的访问数据库的方式和优缺点 1.原始java访问数据库 开发流程麻烦 ...
- ubuntu 上下左右键变成ABCD
1.在ubuntu终端环境出现: 这表示你正在insert mode.... 按esc,回到command mode,上下左右就回复到正常的方向键功能了 2.可能写的程序是在insert mode(r ...
- Mask RCNN 学习笔记
下面会介绍基于ResNet50的Mask RCNN网络,其中会涉及到RPN.FPN.ROIAlign以及分类.回归使用的损失函数等 介绍时所采用的MaskRCNN源码(python版本)来源于GitH ...
- 电脑kail linux 连接手机Nethunter,手机和电脑互传文件
1.开启nethunter的ssh 修改/etc/ssh/sshd_config 参考:解决kali linux 开启ssh服务后连接不上的问题 2.如果在手机终端修改不了(我的就是怎么也改不了),可 ...
- struct 与 class 的区别
C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能. struct能包含成员函数吗? 能! struct能继承吗? 能!! s ...
- ubuntu下安装intel realsense驱动
在安装之前一定要确保系统是ubuntu 14.04.3 64位! 由于一开始安装的是32位系统,导致在升级内核版本到4.4时各种问题,最终靠重装系统解决. 因为intel给出的测试代码均是在64位14 ...
- 【转】Shell编程基础篇-上
[转]Shell编程基础篇-上 1.1 前言 1.1.1 为什么学Shell Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具, Linux/UNIX系统的底层及基础应 ...
- NMS和soft-nms算法
非极大值抑制算法(nms) 1. 算法原理 非极大值抑制算法(Non-maximum suppression, NMS)的本质是搜索局部极大值,抑制非极大值元素. 2. 3邻域情况下NMS的实现 3邻 ...
- XAF 与 CIIP
XAF 与 CIIP:网站:http://www.uims.top, XAF技术博客:http://www.cnblogs.com/foreachlife/ tylike 升级到 DevExpres ...