Go语言中的并发与多线程:进阶篇
\n\n并发(concurrency)是Go语言的关键特性之一,它在运行时和语法层面被内置于语言架构中。与许多其他语言不同,这些语言的多线程通过复杂的库实现,并需要手动管理线程,而Go提供了轻量级的goroutine和channel。在进阶层面,重要的不仅仅是启动多个任务,而是合理地同步它们,避免数据竞争,并高效利用系统资源。在本文中,我们将探讨Go语言中并发编程的进阶技巧。
\n\n1. Goroutine:生命周期管理与优先级
\n\nGoroutine是由Go运行时管理的轻量级线程。它们通过关键字go启动。然而,在进阶层面,你需要理解如何控制它们的执行与结束。
基本的goroutine启动:
\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc worker(id int) {\n fmt.Printf("Worker %d starting\\n", id)\n time.Sleep(time.Second)\n fmt.Printf("Worker %d done\\n", id)\n}\n\nfunc main() {\n for i := 1; i <= 5; i++ {\n go worker(i)\n }\n time.Sleep(2 * time.Second) // 临时方案:等待完成\n}\n\n问题:在这个例子中,我们使用time.Sleep来等待,这并不可靠。在进阶层面,会使用channel和sync.WaitGroup。
2. 使用sync.WaitGroup进行同步
\n\nsync.WaitGroup允许你等待指定数量的goroutine完成。这是一个基础但强大的同步工具。
package main\n\nimport (\n "fmt"\n "sync"\n "time"\n)\n\nfunc worker(id int, wg *sync.WaitGroup) {\n defer wg.Done() // 完成时减少计数器\n fmt.Printf("Worker %d starting\\n", id)\n time.Sleep(time.Second)\n fmt.Printf("Worker %d done\\n", id)\n}\n\nfunc main() {\n var wg sync.WaitGroup\n for i := 1; i <= 5; i++ {\n wg.Add(1) // 启动前增加计数器\n go worker(i, &wg)\n }\n wg.Wait() // 阻塞直到计数器归零\n fmt.Println("All workers completed")\n}\n\n重要提示:wg.Add()必须在goroutine启动前调用,否则可能导致数据竞争。在goroutine内部使用defer wg.Done()以确保即使在发生panic时也能减少计数器。
3. Channel:缓冲、方向与select
\n\nChannel是goroutine之间数据交换的主要机制。在进阶层面,会使用缓冲channel、单向channel以及select语句。
3.1. 缓冲channel
\n缓冲channel允许在不立即接收的情况下发送数据。这减少了阻塞并提高了吞吐量。
\npackage main\n\nimport "fmt"\n\nfunc main() {\n ch := make(chan int, 3) // 缓冲区大小为3的channel\n ch <- 1\n ch <- 2\n ch <- 3\n // ch <- 4 // 阻塞,因为缓冲区已满\n fmt.Println(<-ch)\n fmt.Println(<-ch)\n fmt.Println(<-ch)\n}\n\n3.2. 单向channel
\n为了类型安全和明确指定channel的角色,可以使用单向channel:chan<-(仅发送)和<-chan(仅接收)。
package main\n\nimport "fmt"\n\nfunc sender(out chan<- int) {\n for i := 0; i < 5; i++ {\n out <- i\n }\n close(out)\n}\n\nfunc receiver(in <-chan int) {\n for val := range in {\n fmt.Println(val)\n }\n}\n\nfunc main() {\n ch := make(chan int)\n go sender(ch)\n receiver(ch)\n}\n\n3.3. select语句
\nselect允许一个goroutine同时等待多个channel。这是