方向3 实践记录以及工具使用
goroutine之间的通信
在 Go 语言中,Goroutine 是一种轻量级的线程。Goroutine 之间的通信方式以 channel 为主,提供了一种线程安全、简洁易用的机制来实现协作和数据交换。
1. Goroutine 通信的主要方式
1.1 使用 Channel
Channel 是 Go 提供的内置类型,用于在多个 Goroutine 之间传递数据。它是线程安全的,并且通过 阻塞机制 实现同步。Channel 可以是无缓冲的(同步传输)或有缓冲的(异步传输)。
• 创建 Channel:
ch := make(chan int) // 无缓冲 Channel
ch := make(chan int, 5) // 有缓冲 Channel,缓冲区大小为 5
• 发送和接收数据:
ch <- value // 发送数据到 channel
val := <-ch // 从 channel 接收数据
• 关闭 Channel: 使用 close 关闭一个 channel,表示发送方不会再发送数据了。
close(ch)
例子:两个 Goroutine 之间的简单通信
package main
import "fmt"
func worker(ch chan int) {
for {
value, ok := <-ch
if !ok { // channel 关闭,退出
fmt.Println("Channel closed!")
return
}
fmt.Println("Received:", value)
}
}
func main() {
ch := make(chan int)
go worker(ch)
for i := 1; i <= 5; i++ {
ch <- i
}
close(ch) // 通知 worker Goroutine 数据传输完成
}
运行结果:
Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Channel closed!
1.2 使用 Select 监听多个 Channel
select 是 Go 中用于监听多个 channel 的语法结构。它会阻塞,直到某个 channel 可以发送或接收数据。 例子:监听多个 channel
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch1 <- "message from ch1"
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- "message from ch2"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("Received:", msg1)
case msg2 := <-ch2:
fmt.Println("Received:", msg2)
}
}
}
运行结果:
Received: message from ch2
Received: message from ch1
1.3 使用 Mutex 和共享变量
除了 Channel 外,Go 还提供了 sync.Mutex 来实现 Goroutine 之间的同步,保护共享资源。
例子:使用 Mutex 实现 Goroutine 间的安全共享
package main
import (
"fmt"
"sync"
)
var counter int
var mutex sync.Mutex
func worker(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
mutex.Lock()
counter++
mutex.Unlock()
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(&wg)
}
wg.Wait()
fmt.Println("Final Counter:", counter)
}
1.4 使用 WaitGroup 等待多个 Goroutine 完成
sync.WaitGroup 用于等待一组 Goroutine 完成任务。
例子:使用 WaitGroup 等待 Goroutine
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d started\n", id)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // 等待所有 Goroutine 完成
fmt.Println("All workers completed!")
}
运行结果:
Worker 1 started
Worker 1 done
Worker 2 started
Worker 2 done
...
All workers completed!
2. Goroutine 通信的常见模式
2.1 管道(Pipeline)模式 将数据处理拆分为多个阶段,每个阶段由 Goroutine 完成,数据通过 channel 在阶段之间传递。
例子:
package main
import "fmt"
// Generator:生成一系列整数
func generator(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
// Squarer:平方计算
func squarer(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
nums := generator(2, 3, 4)
squares := squarer(nums)
for sq := range squares {
fmt.Println(sq)
}
}
运行结果:
4
9
16
3. Channel 的一些高级用法
3.1 单向 Channel
• 发送专用:chan<- T
• 接收专用:<-chan T
例子:
func send(ch chan<- int) {
ch <- 42
}
func receive(ch <-chan int) {
fmt.Println(<-ch)
}
func main() {
ch := make(chan int)
go send(ch)
receive(ch)
}
3.2 带缓冲的 Channel
缓冲区允许 channel 存储多条数据,发送方无需等待接收方立即接收。
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(<-ch) // 输出: 1
个人总结
• Channel 是 Goroutine 通信的核心工具,适合传递数据和实现同步。
• select 可以监听多个 channel,让程序更具灵活性。
• WaitGroup 和 Mutex 等工具补充了更细粒度的控制和同步。
• 高效使用 Goroutine 和通信机制需要结合场景,合理选择模型。