Go语言并发编程Goroutine与Channel详解1. Goroutine简介Goroutine是Go语言的核心特性之一它是一种轻量级的线程由Go运行时runtime管理。与传统操作系统线程相比Goroutine的创建和销毁成本极低可以在数千个Goroutine之间快速切换这使得Go语言能够高效处理高并发场景。1.1 创建Goroutine在Go语言中只需使用go关键字即可创建一个Goroutinefunc main() { go sayHello() fmt.Println(Main function) } func sayHello() { fmt.Println(Hello, Goroutine!) }1.2 Goroutine的特点轻量级初始栈大小仅为2KB可动态增长由Go运行时管理无需手动管理线程创建和销毁高效的调度Go运行时使用M-P-G模型M个线程、P个处理器、G个Goroutine进行高效调度支持并发而非并行通过GMP模型实现高效的并发处理2. Channel简介Channel是Go语言用于Goroutine之间通信的机制它提供了一种安全、高效的数据传递方式。通过Channel不同的Goroutine可以安全地进行数据交换避免了传统并发编程中的锁和竞态条件问题。2.1 创建Channel// 创建无缓冲Channel ch : make(chan int) // 创建有缓冲Channel ch : make(chan int, 10)2.2 Channel操作// 发送数据 ch - value // 接收数据 value : -ch // 关闭Channel close(ch)3. 无缓冲Channel与有缓冲Channel3.1 无缓冲Channel无缓冲Channel没有缓冲区发送和接收操作必须同时准备才能成功。这种特性使得无缓冲Channel非常适合用于Goroutine之间的同步func worker(done chan bool) { fmt.Println(Worker is working...) time.Sleep(2 * time.Second) done - true } func main() { done : make(chan bool) go worker(done) -done // 等待worker完成 fmt.Println(Worker finished) }3.2 有缓冲Channel有缓冲Channel带有缓冲区发送操作在缓冲区满之前不会阻塞接收操作在缓冲区空之前不会阻塞func main() { ch : make(chan int, 3) // 发送数据不会阻塞直到缓冲区满 ch - 1 ch - 2 ch - 3 // 再发送会阻塞 // ch - 4 // 接收数据 fmt.Println(-ch) // 1 fmt.Println(-ch) // 2 }4. Channel方向与Select4.1 Channel方向在函数参数中可以指定Channel的方向实现只发送或只接收// 只发送Channel func sendData(ch chan- int) { ch - 100 } // 只接收Channel func receiveData(ch -chan int) { data : -ch fmt.Println(data) }4.2 Select语句Select语句用于在多个Channel操作中选择一个可以立即执行的分支类似于switch但作用于Channelfunc main() { ch1 : make(chan string) ch2 : make(chan string) go func() { time.Sleep(1 * time.Second) ch1 - Channel 1 }() go func() { time.Sleep(2 * time.Second) ch2 - Channel 2 }() for i : 0; i 2; i { select { case msg1 : -ch1: fmt.Println(msg1) case msg2 : -ch2: fmt.Println(msg2) case -time.After(3 * time.Second): fmt.Println(Timeout) } } }5. 最佳实践5.1 避免Channel泄漏Channel泄漏是指发送端或接收端被永久阻塞导致Goroutine无法退出。在使用Channel时应该确保发送端在数据发送完毕后关闭Channel接收端使用range遍历Channel直到Channel关闭使用select语句和default分支处理超时和退出// 正确的Channel遍历方式 func consume(ch -chan int) { for v : range ch { fmt.Println(v) } }5.2 使用WaitGroup等待Goroutine完成使用sync.WaitGroup可以更方便地等待多个Goroutine完成func main() { var wg sync.WaitGroup for i : 0; i 5; i { wg.Add(1) go func(id int) { defer wg.Done() fmt.Printf(Goroutine %d finished\n, id) }(i) } wg.Wait() fmt.Println(All goroutines finished) }5.3 防止Panic在多个Goroutine中如果其中一个发生Panic其他Goroutine不会自动捕获。因此在Goroutine中应该使用recover来捕获Panicfunc safeGo(fn func()) { go func() { defer func() { if r : recover(); r ! nil { fmt.Println(Recovered from panic:, r) } }() fn() }() }6. 总结Goroutine和Channel是Go语言并发编程的核心它们提供了一种简洁、安全、高效的并发处理方式。通过合理地使用Goroutine和Channel可以轻松实现高并发的应用程序。在实际开发中应该注意避免Channel泄漏使用WaitGroup管理Goroutine的生命周期并在Goroutine中适当处理Panic以确保程序的稳定性和可靠性。