悠悠楠杉
如何在Golang中实现基础的goroutine并发执行
在现代软件开发中,高并发处理能力已成为衡量程序性能的重要指标之一。Golang(Go语言)自诞生起便以“为并发而生”著称,其核心特性之一——goroutine,让开发者能够轻松编写高效、简洁的并发程序。相比传统线程,goroutine轻量得多,启动成本极低,成千上万个goroutine同时运行也不会造成系统资源枯竭。本文将深入探讨如何在Golang中实现基础的goroutine并发执行,帮助初学者理解其工作原理并掌握常见用法。
Goroutine是Go运行时管理的轻量级线程,由Go调度器(scheduler)负责在多个操作系统线程上复用和调度。我们只需在函数调用前加上go关键字,即可启动一个goroutine。例如:
go
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello from goroutine")
}
func main() {
go sayHello()
time.Sleep(100 * time.Millisecond) // 等待goroutine执行完成
fmt.Println("Main function ends")
}
上述代码中,go sayHello()会立即返回,主函数继续执行。由于goroutine是异步执行的,若不加等待,主函数可能在sayHello执行前就退出了,导致看不到输出。因此我们使用time.Sleep进行简单延时。但在实际项目中,这种做法并不推荐,因为它不可靠且难以控制。
更优雅的方式是使用sync.WaitGroup来同步多个goroutine的完成状态。WaitGroup可以等待一组goroutine全部执行完毕。基本用法如下:
go
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 任务完成时通知
fmt.Printf("Worker %d is working\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait() // 阻塞直到所有worker完成
fmt.Println("All workers done")
}
在这个例子中,每次启动goroutine前调用wg.Add(1),表示增加一个待完成的任务;在每个goroutine内部通过defer wg.Done()确保任务完成后通知。主函数调用wg.Wait()会一直阻塞,直到所有任务完成。
除了WaitGroup,Go还提供了channel作为goroutine之间通信的主要机制。Channel不仅可以传递数据,还能用于同步。无缓冲channel会在发送和接收时阻塞,天然具备同步能力。例如:
go
package main
import "fmt"
func sendData(ch chan string) {
ch <- "Data from goroutine"
}
func main() {
ch := make(chan string)
go sendData(ch)
msg := <-ch // 接收数据,阻塞直到有数据可读
fmt.Println(msg)
}
这里创建了一个字符串类型的channel,子goroutine向其中发送数据,主函数从中接收。由于是无缓冲channel,发送操作会阻塞,直到有接收方准备好,从而实现了同步。
对于需要控制并发数量的场景,可以使用带缓冲的channel作为信号量。比如限制最多同时运行3个任务:
go
package main
import (
"fmt"
"sync"
)
func limitedWorker(taskID int, sem chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
sem <- struct{}{} // 获取信号量
fmt.Printf("Task %d running\n", taskID)
// 模拟工作
<-sem // 释放信号量
}
func main() {
const maxConcurrent = 3
sem := make(chan struct{}, maxConcurrent)
var wg sync.WaitGroup
for i := 1; i <= 10; i++ {
wg.Add(1)
go limitedWorker(i, sem, &wg)
}
wg.Wait()
}
通过容量为3的channel,我们确保任何时候最多只有3个goroutine在执行任务,避免资源过载。
总结来看,Golang的goroutine极大简化了并发编程。结合sync.WaitGroup、channel等工具,开发者可以灵活构建各种并发模型。掌握这些基础方法,是迈向高效Go程序设计的第一步。
