网站首页 > 技术文章 正文
引言
现在还不能掌握并发编程的程序员,面临被计算机技术淘汰的窘境。本文注重介绍go语言的goroutine实现并发的编程。
什么是GoRoutine
Goroutine是与其他函数或方法并发运行的函数或方法,可以将Goroutine视为轻量级线程。与线程相比,创建Goroutine的成本开销微乎其微。因此,GO应用程序通常同时运行数千个Goroutine。
与线程相比,Goroutine的优势
- 与线程相比,Goroutine非常节约CPU和内存。它们的堆栈大小只有几KB,堆栈可以根据应用程序的需要进行扩展和缩小,而对于线程,必须指定并固定堆栈大小。
- Goroutine被多路复用到较少数量的OS线程。具有数千个Goroutine的程序中可能只有一个线程。如果线程中任何Goroutine阻塞并等待用户输入,则创建另一个OS线程,并将剩余的Goroutine移到新的OS线程。所有这些都由运行时(runtime)负责,作为程序员从这些错综复杂的细节中抽象出来,并被赋予一个干净的API来处理并发性。
- Goroutine使用通道(channel)进行通信。通道的设计可以防止在使用Goroutine访问共享内存时出现争用情况。可以将通道视为Goroutines通信所使用的管道。我们将在下一教程中详细讨论频道。
开始使用GoRoutine
在函数或方法调用前面加上关键字go,您将同时运行一个新的Goroutine。
让我们创建一个Goroutine:)
package main
import (
"fmt"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
在第11行,go hello()开始一个新的Goroutine,hello()函数将与main()函数并发运行。main函数在它自己的Goroutine中运行,它被称为main Goroutine。
运行这个程序,你会有惊喜哦!
本程序仅输出了文本函数。那我们开始的Goroutine怎么样了?我们需要了解goroutine的两个重要特性,才能理解为什么会发生这种情况。
- 当启动新的Goroutine时,goroutine调用立即返回。与函数不同,该控件不等待Goroutine完成执行。在Goroutine调用之后,程序立即返回到下一行代码,并忽略来自Goroutine的任何返回值。
- 主Goroutine应该为任何其他Goroutine运行。如果主Goroutine终止,则该程序将终止,并且不会运行其他Goroutine。
我想现在你可以理解为什么我们的Goroutine没有跑了。
在第11行调用go hello()之后,程序立即返回到下一行代码,而无需等待hello goroutine完成。
然后,主Goroutine终止,因为没有其他代码要执行,因此hello Goroutine没有机会运行。
我们对代码稍作修改。
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
在上述程序的第13行中,我们调用了time包的Sleep方法,该方法使执行该程序的go例程延时。在这种情况下,主Goroutine会休眠1秒。
现在,go hello()调用在主Goroutine终止之前有足够的时间执行。此程序首先打印Hello world goroutine,等待1秒,然后打印main函数。
这种在主Goroutine中使用睡眠来等待其他Goroutine完成执行的方式,是我们用来理解Goroutine如何工作的一种技巧。通道(channel)可以用来阻塞主Goroutine,直到所有其他Goroutine完成执行。
启动多个Goroutine
让我们再编写一个启动多个Goroutine的程序,以便更好地理解Goroutine。
package main
import (
"fmt"
"time"
)
func numbers() {
for i := 1; i <= 5; i++ {
time.Sleep(250 * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(400 * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go numbers()
go alphabets()
time.Sleep(3000 * time.Millisecond)
fmt.Println("main terminated")
}
上面的程序在行号中启动两个Goroutine,代码21和22行。这两个Goroutine现在同时运行。
Goroutine numbers 最初休眠250毫秒,然后打印1,然后再次休眠并打印2,相同的周期发生,直到打印5。类似地,Goroutine alphabets 打印从a到e的字母,并且有400毫秒间隔的休眠时间。
主Goroutine启动numbers() ,alphabets(),然后休眠3000毫秒,然后终止。
上述代码运行后输出内容如下:
1 a 2 3 b 4 c 5 d e main terminated
代码不如图片来的直观,我们使用下图描述该程序的工作方式。
蓝色的图像的第一部分代表 numbers Goroutine,栗色的第二部分代表 alphabets Goroutine,绿色的第三部分代表 main Goroutine,最后的部分将上述三部分合并在一起,并向我们展示了程序是如何工作的。
每个框顶部的0毫秒、250毫秒等表示时间(以毫秒为单位),输出在每个框的底部表示为1、2、3,依此类推。
蓝色框告诉我们,1在250ms之后打印,2在500ms之后打印,依此类推。最后一个综合框的底部有值1a23b4c5de,它也是程序的输出。
直观的时序图将帮助你更好地理解GoRoutine运行的机制。
Happy Coding :-)
我是 @程序员小助手 ,持续分享编程故事,欢迎关注。
猜你喜欢
- 2024-12-01 Go 并发可视化解释 — 通道
- 2024-12-01 Go并发编程面试15题
- 2024-12-01 Go 语言并发编程实践:从入门到进阶
- 2024-12-01 Go语言编写的简单并发编程示例
- 2024-12-01 并发编程的奇技淫巧:Go语言调度器的内在工作机制
- 2024-12-01 Goroutine 并发调度模型深度解析之手撸一个高性能 goroutine 池
- 2024-12-01 每天2分钟学习GO语言编程(十九)并发简明教程
- 2024-12-01 Go项目中如何限制并发数?Atomic必须掌握
- 2024-12-01 Go语言并发入门
- 2024-12-01 Go 语言 Goroutines 协程并发
- 1509℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 522℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 491℃MySQL service启动脚本浅析(r12笔记第59天)
- 470℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 468℃启用MySQL查询缓存(mysql8.0查询缓存)
- 448℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 428℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 425℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)