第23天:并发基础:Goroutines
欢迎来到Go语言学习的第23天!今天我们将深入探讨Goroutines的概念,这是Go语言并发编程的核心要素。理解Goroutines的工作原理和使用方法,将使你能够编写高效的并发应用程序。
1. 并发和并行的概念
在深入Goroutines之前,我们需要清楚理解并发与并行的区别:
- 并发(Concurrency)是指多个任务在同一时间段内进行调度,但并不一定同时执行。
- 并行(Parallelism)则是指多个任务在同一时刻真正同时执行。
Go语言的并发机制主要通过Goroutines实现。
1.1 Goroutines与传统线程的对比
特性 | Goroutines | 传统线程 |
---|---|---|
创建开销 | 轻量级,创建简单 | 开销较大,创建复杂 |
管理 | 由Go运行时(Go runtime)管理 | 需要操作系统调度 |
内存占用 | 每个Goroutine占用约2KB | 每个线程占用更多内存 |
数量 | 可以同时运行数万个Goroutines | 受限于操作系统,通常为数百个 |
2. 什么是Goroutines
Goroutines是Go语言提供的轻量级线程,用于并发执行。Goroutines的创建和调度非常高效,因此开发人员可以创建成千上万的Goroutines。
2.1 创建Goroutine
在Go中,通过使用 go
关键字来创建一个Goroutine。例如:
go func() {
fmt.Println("Hello from Goroutine")
}()
上面的代码创建了一个新的Goroutine,运行一个匿名函数。
3. 设置Goroutines示例
接下来,我们来看一个使用Goroutines的简单示例,其中我们将启动多个Goroutines来并发执行任务。
3.1 示例代码
package main
import (
"fmt"
"time"
)
func printNumbers(from, to int) {
for i := from; i <= to; i++ {
fmt.Println(i)
time.Sleep(100 * time.Millisecond) // 模拟耗时操作
}
}
func main() {
fmt.Println("Starting Goroutines")
go printNumbers(1, 5) // 启动第一条Goroutine
go printNumbers(6, 10) // 启动第二条Goroutine
// 主Goroutine等待输入,以保持程序运行
var input string
fmt.Scanln(&input)
}
3.2 代码说明
printNumbers
函数:打印从from
到to
的数字,并在每次打印后暂停100毫秒,模拟一些耗时操作。main
函数:- 使用
go
关键字启动两个Goroutines,分别打印1到5和6到10的数字。 - 主Goroutine使用
fmt.Scanln
保持运行,直到有用户输入,以便等待Goroutines完成。
- 使用
3.3 代码运行流程图
下面是代码执行流程的示意图:
+---------------------+
| main() |
+---------------------+
| Start Goroutine 1 |
| start printNumbers(1,5) |
+---------------------+
| Start Goroutine 2 |
| start printNumbers(6,10) |
+---------------------+
| Wait for user input |
+---------------------+
|
v
+---------------------+
| printNumbers(1,5) |
| |
+---------------------+
| printNumbers(6,10) |
| |
+---------------------+
3.4 运行示例
在项目目录下运行该程序:
go run main.go
你应该能够看到1到10的数字交替打印,表示两个Goroutines正并发执行。
4. Goroutines的调度
Goroutines的调度是由Go的运行时系统自动管理的。它会在适当的时候将Goroutines从一个操作系统线程切换到另一个线程,确保尽可能高效地利用CPU资源。
4.1 Go调度器
Go调度器基于M:N调度模型,其中M是操作系统线程的数量,N是Goroutines的数量。Go会根据需要在有限的线程资源中调度多个Goroutines。
5. Goroutines的特性
5.1 Goroutines是非阻塞的
Goroutines是非阻塞的,即当一个Goroutine在执行某个操作时,其他Goroutines仍然可以继续执行。
5.2 Goroutines的生命周期
- Goroutines开始时由
go
关键字触发。 - 当Goroutine退出时(执行完毕或发生错误),其资源会被自动释放。
6. 管理Goroutines
在有些情况下,我们需要控制Goroutines的数量,或等待它们完成。常用的方法有使用WaitGroup与Channel。
6.1 使用WaitGroup
示例代码:
package main
import (
"fmt"
"sync"
"time"
)
func printNumbers(from, to int, wg *sync.WaitGroup) {
defer wg.Done() // 在函数结束时调用Done
for i := from; i <= to; i++ {
fmt.Println(i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
var wg sync.WaitGroup
fmt.Println("Starting Goroutines")
wg.Add(2) // 添加2个Goroutine
go printNumbers(1, 5, &wg)
go printNumbers(6, 10, &wg)
wg.Wait() // 等待所有Goroutines完成
fmt.Println("All Goroutines completed")
}
6.2 代码说明
- 使用
sync.WaitGroup
来管理多个Goroutines的执行。 - 在每个Goroutine中,调用
wg.Done()
表示完成该Goroutine的任务。 - 在主Goroutine中,调用
wg.Wait()
来等待所有的Goroutines完成。
6.3 运行输出
你可以运行这个示例并观察输出,程序将在所有Goroutines完成后打印“所有Goroutines完成”。
7. 使用Channel与Goroutines
Channel是Go语言中用于在Goroutines之间通信的同步原语。通过Channel,你可以在多个Goroutines之间安全地传递数据。
7.1 基本用法
示例代码:
package main
import (
"fmt"
"time"
)
func printNumbers(ch chan int) {
for i := 1; i <= 5; i++ {
time.Sleep(100 * time.Millisecond)
ch <- i // 发送数据到Channel
}
close(ch) // 关闭Channel
}
func main() {
ch := make(chan int) // 创建Channel
go printNumbers(ch) // 启动Goroutine
// 从Channel读取数据
for num := range ch {
fmt.Println(num)
}
fmt.Println("All numbers printed")
}
7.2 代码说明
printNumbers
函数向 Channel 中发送1到5之间的数字,并最终关闭Channel。- 主Goroutine从Channel中读取数据,直到Channel关闭。
7.3 代码运行流程图
+---------------------+
| main() |
+---------------------+
| Create Channel |
+---------------------+
| Start Goroutine |
| printNumbers(ch) |
+---------------------+
|
v
+---------------------+
| printNumbers |
| |
| Send numbers to |
| Channel |
+---------------------+
|
v
+---------------------+
| Read from Channel |
| Print numbers |
+---------------------+
8. 总结与练习
在今天的学习中,我们深入理解了Goroutines的概念及其重要性。Goroutines允许我们轻松编写并发程序,而无需涉及复杂的线程管理。
练习
- 创建一个程序,启动多个Goroutines,并以随机顺序打印字母A到Z。
- 使用Channel实现一个Goroutine生产者-消费者模型,生产者生成数字,消费者打印数字。
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!