Go语言异步编程:初学者完全指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Go语言异步编程:初学者完全指南

Go(Golang)在设计之初就充分考虑了现代开发需求,其中性能与并行性扮演着关键角色。与许多通过回调或Promise实现异步的编程语言不同,Go提供了内置且优雅的机制:goroutine和channel。本指南将为您介绍Go异步编程的基础知识,并展示如何编写高效的并发代码。



什么是goroutine?它们与线程有何不同?

Goroutine是由Go运行时管理的轻量级执行线程。与操作系统级线程不同,goroutine消耗的资源要少得多:其栈空间起始只有几KB,并且可以动态增长和收缩。这使得您可以同时运行数百万个goroutine而不会造成显著的内存负担。

启动一个goroutine非常简单——只需在函数调用前加上关键字go:

package main

import ( "fmt" "time")

func sayHello() { fmt.Println("来自goroutine的问候!")}

func main() { go sayHello() // 启动一个goroutine time.Sleep(100 * time.Millisecond) // 等待goroutine执行 fmt.Println("主函数执行完毕")}

需要注意的是:如果主函数main()执行结束,所有正在运行的goroutine都会被强制停止。为此我们需要使用专门的同步工具。



使用WaitGroup进行同步

等待所有goroutine执行完毕的最简单方法是使用sync.WaitGroup。它的工作原理类似一个计数器:启动goroutine时递增计数器,完成时递减计数器。主函数会阻塞直到计数器归零。

package main

import ( "fmt" "sync" "time")

func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // 完成时递减计数器 fmt.Printf("工作者 %d 开始工作", id) time.Sleep(time.Second) fmt.Printf("工作者 %d 完成工作", id)}

func main() { var wg sync.WaitGroup

for i := 1; i <= 3; i++ { wg.Add(1) // 启动前递增计数器 go worker(i, &wg) }

wg.Wait() // 等待所有goroutine完成 fmt.Println("所有工作者已完成工作")}

注意:wg.Add(1)应在启动goroutine之前调用,而wg.Done()应在goroutine内部调用(通常通过defer)。这样可以确保即使在发生panic的情况下计数器也能正确递减。



Channel:goroutine之间的通信

Channel(通道)是类型化的"管道",goroutine可以通过它安全地交换数据。Channel默认提供同步机制:向channel发送数据会阻塞当前goroutine,直到另一个goroutine读取该值;反之亦然。

使用make函数创建channel:

package main

import "fmt"

func main() { // 创建一个用于整数的无缓冲channel ch := make(chan int)

// 启动一个发送数据的goroutine go func() { ch <- 42 // 向channel发送一个值 }()

// 从channel读取数据(会阻塞直到有数据到达) value := <-ch fmt.Println("接收到的值:", value)}

Channel可以分为:

  • 无缓冲——同步的,发送操作会阻塞直到被接收。
  • 有缓冲——异步的,发送操作只会在缓冲区满时阻塞。
// 创建容量为3的有缓冲channelbufferedCh := make(chan string, 3)bufferedCh <- "A"bufferedCh <- "B"bufferedCh <- "C" // 缓冲区未满前不会阻塞


博客

书籍推荐