目录
Goroutines
-
定义与特点:
- Goroutines是Go语言中实现并发的基本单位。
- 它比传统的线程更轻量级,拥有更小的内存占用和更快的启动时间。
- 在Go程序中,您可以轻松地启动成千上万的Goroutines。
-
使用方法:
- 使用
go
关键字后跟一个函数调用,即可启动一个Goroutine。 - 例如:
go myFunction()
- 使用
package main
import (
"fmt"
"time"
)
// 定义一个简单的函数
func printNumbers(prefix string) {
for i := 1; i <= 5; i++ {
fmt.Println(prefix, i)
// 休眠一段时间,以模拟实际操作中的耗时
time.Sleep(time.Millisecond * 500)
}
}
func main() {
// 使用 go 关键字启动一个新的 Goroutine
go printNumbers("Goroutine")
// 主Goroutine也执行相同的函数
printNumbers("Main")
// 等待足够长的时间以确保Goroutine完成
// 注意:这不是同步Goroutines的推荐方式
// 后续课程将介绍更好的方法(如WaitGroup或Channel)
time.Sleep(time.Second * 3)
fmt.Println("主函数执行完毕")
}
- 在实际应用中,我们通常不使用
time.Sleep
来等待Goroutines完成,而是使用像通道(Channels)或sync.WaitGroup
这样的同步机制。 - 这个示例的目的是简单地展示Goroutines的并发执行。在后续课程中,我们将探讨更高级的同步技术和并发模式。
通道(Channel)
-
定义与作用:
- Channel是Go语言中的一种内置类型,用于在Goroutines之间安全地传递数据。
- 它可以帮助解决Goroutines之间的同步问题。
-
类型与使用:
- 有缓冲和无缓冲两种类型的Channel。
- 创建示例:
ch := make(chan int)
(无缓冲)或ch := make(chan int, 5)
(有缓冲,容量为5)。 - 数据传递:使用
<-
操作符向Channel发送或接收数据。
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个无缓冲的Channel
ch := make(chan string)
// 启动一个Goroutine,发送数据到Channel
go func() {
fmt.Println("Goroutine开始发送数据")
ch <- "从Goroutine传来的消息"
}()
// 模拟延时,表示主Goroutine正在处理其他任务
time.Sleep(2 * time.Second)
// 从Channel接收数据
message := <-ch
fmt.Println("接收到数据:", message)
// 创建一个有缓冲的Channel,容量为2
bufferedCh := make(chan int, 2)
// 向有缓冲的Channel发送数据,不会立即阻塞
bufferedCh <- 1
bufferedCh <- 2
// 从有缓冲的Channel接收数据
fmt.Println("从有缓冲Channel接收数据:", <-bufferedCh)
fmt.Println("从有缓冲Channel接收数据:", <-bufferedCh)
}
- 无缓冲Channel在没有接收者时,发送操作会阻塞;有缓冲Channel则在缓冲区满时才会阻塞。
- Channel的正确使用是并发编程中非常重要的部分,需要仔细处理发送和接收的同步问题。
代码示例
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
// 数据处理函数
// 模拟接收一个整数,经过处理后发送到输出Channel
func processData(id int, data int, out chan<- string) {
// 模拟数据处理耗时
time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
result := fmt.Sprintf("Goroutine %d 处理结果:%d", id, data*2) // 假设处理是简单的乘以2
out <- result
}
func main() {
// 初始化随机种子
rand.Seed(time.Now().UnixNano())
// 创建一个无缓冲的Channel
dataCh := make(chan string)
// 使用WaitGroup等待所有Goroutines完成
var wg sync.WaitGroup
// 启动多个Goroutines进行数据处理
const numGoroutines = 5 // Goroutines的数量
for i := 0; i < numGoroutines; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
data := rand.Intn(100) // 生成一个随机数作为数据
processData(id, data, dataCh)
}(i)
}
// 启动一个Goroutine用于接收所有处理结果
go func() {
wg.Wait()
close(dataCh)
}()
// 从Channel中读取并打印每个Goroutine的处理结果
for result := range dataCh {
fmt.Println(result)
}
fmt.Println("所有Goroutine处理完成")
}
-
processData
函数:每个Goroutine都会调用这个函数,它接收一个整数数据,处理后(这里简化为乘以2的操作),将结果发送到输出Channel。 -
启动多个Goroutines:循环中创建了多个Goroutines,每个都调用
processData
函数处理数据。 -
使用
sync.WaitGroup
:WaitGroup
用于等待所有Goroutine完成其工作。每启动一个Goroutine,就调用wg.Add(1)
,每个Goroutine完成时调用wg.Done()
。 -
关闭Channel:在所有Goroutines完成后,关闭数据Channel。这是通过在另一个Goroutine中调用
wg.Wait()
和close(dataCh)
实现的。 -
读取Channel数据:主Goroutine循环读取Channel中的数据,并打印结果。