Go中的并发模型:Goroutine和Channel
字数 962 2025-11-01 23:47:50
Go中的并发模型:Goroutine和Channel
Go的并发模型基于Goroutine(轻量级线程)和Channel(通道),通过它们可以高效安全地实现并发编程。下面逐步讲解其核心概念和用法。
1. Goroutine:轻量级并发单元
描述:
Goroutine是Go语言中的并发执行单元,比操作系统线程更轻量(初始栈约2KB,可动态扩容),由Go运行时调度,而非操作系统内核。
创建与使用:
- 通过
go关键字启动一个Goroutine:go func() { // 并发执行的代码 }() - 示例:主线程与Goroutine并行执行
注意:直接使用package main import ( "fmt" "time" ) func sayHello() { fmt.Println("Hello from Goroutine!") } func main() { go sayHello() // 启动Goroutine time.Sleep(100 * time.Millisecond) // 等待Goroutine执行(临时方案) fmt.Println("Main function ends.") }Sleep等待Goroutine不严谨,实际需用Channel或sync.WaitGroup同步。
2. Channel:Goroutine间的通信机制
描述:
Channel是类型安全的管道,用于Goroutine间传递数据和同步操作。遵循“不要通过共享内存通信,而要通过通信共享内存”的原则。
基本用法:
- 创建Channel:
ch := make(chan int) // 无缓冲Channel bufferedCh := make(chan int, 3) // 有缓冲Channel(容量为3) - 发送与接收数据:
ch <- 42 // 发送数据到Channel value := <-ch // 从Channel接收数据
无缓冲 vs 有缓冲Channel:
- 无缓冲Channel:发送和接收操作会阻塞,直到另一方准备好(同步通信)。
- 有缓冲Channel:发送方在缓冲区满时阻塞,接收方在缓冲区空时阻塞(异步通信)。
3. 同步示例:使用Channel等待Goroutine结束
package main
import "fmt"
func worker(done chan bool) {
fmt.Println("Working...")
done <- true // 发送完成信号
}
func main() {
done := make(chan bool)
go worker(done)
<-done // 阻塞直到收到信号
fmt.Println("Done.")
}
4. 常见模式:Select多路复用
描述:
select语句监听多个Channel操作,处理多个通信任务。
示例:
ch1, ch2 := make(chan string), make(chan string)
go func() { ch1 <- "from ch1" }()
go func() { ch2 <- "from ch2" }()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}
关键点:
select会阻塞直到某个Case可执行。- 多个Case同时就绪时,随机选择一个执行(避免饥饿)。
5. 避免竞态条件(Race Condition)
问题:多个Goroutine同时修改共享数据可能导致结果不一致。
解决方案:
- 使用Channel传递数据所有权(推荐)。
- 或使用
sync.Mutex加锁:var mu sync.Mutex var counter int func safeIncrement() { mu.Lock() counter++ // 临界区 mu.Unlock() }
6. 实践建议
- 优先使用Channel:简化同步逻辑,避免显式锁。
- 避免Goroutine泄漏:确保每个Goroutine都能退出(如通过
context.Context取消)。 - 谨慎使用全局变量:尽量通过Channel传递数据。
通过以上步骤,你可以理解Goroutine和Channel如何协作构建高效并发程序。