Golang并发基础与goroutine使用方法

Golang并发基础与goroutine使用方法
最新回答
释放

2026-01-07 17:46:57

Golang并发基础与goroutine使用方法

Go语言通过goroutine和channel构建了简单高效的并发模型,其核心优势在于轻量级并发执行与安全通信机制。掌握goroutine的启动、同步控制及数据交换方式,是编写高性能并发程序的关键。

一、Goroutine基础

定义:Goroutine是Go运行时管理的轻量级线程,由Go runtime调度,具有极小的内存开销(初始栈空间通常为2KB,可动态扩展)。与操作系统线程相比,可轻松创建数万个goroutine而不影响性能。

启动方式:在函数调用前添加go关键字即可启动一个goroutine。

func sayHello() { fmt.Println("Hello from goroutine")}func main() { go sayHello() // 启动goroutine time.Sleep(100 * time.Millisecond) // 简单等待(实际开发中不推荐) fmt.Println("Main function")}

注意:直接使用time.Sleep等待goroutine完成仅适用于演示,实际开发需通过同步机制控制生命周期。

二、Goroutine同步控制:sync.WaitGroup

作用:协调多个goroutine的执行,确保主函数等待所有任务完成后再退出。

核心方法:

  • Add(delta int):增加等待计数器。
  • Done():减少计数器(通常通过defer调用)。
  • Wait():阻塞直到计数器归零。

示例:

func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // 任务完成时减少计数器 fmt.Printf("Worker %d startingn", id) time.Sleep(time.Second) fmt.Printf("Worker %d donen", id)}func main() { var wg sync.WaitGroup for i := 1; i <= 3; i++ { wg.Add(1) // 每个goroutine启动前增加计数器 go worker(i, &wg) } wg.Wait() // 阻塞直到所有worker完成 fmt.Println("All workers finished")}

关键点:

  • 必须确保Add的调用次数与Done匹配,否则会导致Wait永久阻塞或提前退出。
  • 使用defer wg.Done()可避免因panic导致计数器未减少的问题。
三、Goroutine间通信:Channel

设计原则:Go提倡“不要通过共享内存来通信,而应该通过通信来共享内存”。Channel是goroutine间安全交换数据的管道。

基本操作:

  • 创建:ch := make(chan Type)(无缓冲)或ch := make(chan Type, bufferSize)(有缓冲)。
  • 发送数据:ch <- value。
  • 接收数据:value := <-ch。
  • 关闭通道:close(ch)(接收方可通过v, ok := <-ch检测通道是否关闭)。

无缓冲通道(同步通信):发送与接收操作会阻塞,直到另一端准备好。

func fetchData(ch chan string) { time.Sleep(2 * time.Second) ch <- "data fetched" // 阻塞直到数据被接收}func main() { ch := make(chan string) go fetchData(ch) result := <-ch // 阻塞直到数据到达 fmt.Println(result)}

有缓冲通道(异步通信):通道可存储固定数量的数据,发送或接收操作仅在缓冲区满/空时阻塞。

func main() { ch := make(chan int, 2) // 缓冲区大小为2 ch <- 1 ch <- 2 fmt.Println(<-ch) // 输出1 fmt.Println(<-ch) // 输出2}四、并发编程最佳实践
  1. 避免主函数提前退出:使用sync.WaitGroup或channel确保goroutine完成执行。
  2. 合理使用缓冲通道:根据场景选择无缓冲(同步)或有缓冲(异步)通道。
  3. 错误处理:通过recover捕获goroutine内的panic,避免程序崩溃。
  4. 资源控制:使用context.Context实现超时控制或取消操作。

完整示例:并发请求与结果汇总

func fetchURL(url string, ch chan<- string) { resp, err := http.Get(url) if err != nil { ch <- fmt.Sprintf("error fetching %s: %v", url, err) return } defer resp.Body.Close() ch <- fmt.Sprintf("success: %s", url)}func main() { urls := []string{"
https://example.com"
, "
https://google.com"
} ch := make(chan string, len(urls)) var wg sync.WaitGroup for _, url := range urls { wg.Add(1) go func(u string) { defer wg.Done() fetchURL(u, ch) }(url) } // 启动一个goroutine等待结果并关闭通道 go func() { wg.Wait() close(ch) }() // 遍历通道结果 for result := range ch { fmt.Println(result) }}

通过掌握goroutine的启动、sync.WaitGroup的同步控制及channel的通信机制,可高效实现Go语言的并发编程。实际开发中需结合场景选择合适的同步与通信方式,确保程序的正确性与性能。