Go 语言并发请求多个 API:Goroutine 与 WaitGroup 实战

宋时安
宋时安
发布时间:2026-10-15| 5人浏览过
原创

本文介绍如何使用 Go 语言的 Goroutine 和 sync.WaitGroup 并发执行多个 HTTP 请求。通过示例代码对比串行与并行调用的差异,讲解 Channel 结果收集与等待组配合使用方法,帮助开发者掌握高效并发处理外部 API 的技巧。

Golang 并发原理简述

Golang 的高性能在很大程度上归功于其轻量级的并发抽象。与 Java 将线程直接映射到操作系统线程不同,Go 使用自身的调度器管理 Goroutine。这种机制使得在单个操作系统线程上调度成千上万个 Goroutine 成为可能。当一个 Goroutine 被阻塞(如等待 I/O)时,调度器会将其挂起并切换到另一个就绪的 Goroutine,从而保持 CPU 核心的高利用率。

串行调用:基础场景

为了理解并发的价值,我们首先看一个串行调用的简单示例。该程序遍历一组 GitHub 用户 URL 并检查连接状态。由于使用了 for 循环逐个执行 http.Get,所有请求必须在前一个完成后才能开始,效率较低。

// 串行调用示例
package main
import (
	"fmt"
	"net/http"
)
func main() {
	links := []string{
		"https://github.com/fabpot",
		"https://github.com/andrew",
		"https://github.com/taylorotwell",
	}
	checkUrls(links)
}

func checkUrls(urls []string) {
	for _, link := range urls {
		checkUrl(link)
	}
}

func checkUrl(url string) {
	_, err := http.Get(url)
	if err != nil {
		fmt.Println("Failed:", url)
	} else {
		fmt.Println("Success:", url)
	}
}

高性能并发调用实现

要实现并发调用,核心在于三个要素:启动 Goroutine、使用 Channel 传递结果、以及利用 WaitGroup 同步等待所有任务完成。

第1步:初始化并发工具

在并发函数中,我们需要创建两个关键结构:

  • c := make(chan string):定义一个字符串类型的 Channel,用于从子 Goroutine 向主程序传递结果。发送操作使用 <- 符号,例如 c <- result。
  • var wg sync.WaitGroup:引入标准库 sync 中的 WaitGroup,用于跟踪正在运行的任务数量,防止主程序过早退出。

第2步:启动 Goroutine 并计数

在遍历 URL 的请求循环中,不再直接调用处理函数,而是使用 go 关键字启动新的 Goroutine。同时,每次启动前必须调用 wg.Add(1) 以递增等待计数器,确保主程序知道还有多少任务待完成。

func checkUrls(urls []string) {
	c := make(chan string)
	var wg sync.WaitGroup

	// 启动携程并记录任务数
	for _, link := range urls {
		wg.Add(1)
		go checkUrl(link, c, &wg)
	}
	...
}

第3步:同步等待并关闭通道

这是一个容易出错的环节。如果直接在主 Goroutine 中读取 Channel,而子任务尚未结束,主程序可能会阻塞或提前退出。解决方案是开启一个新的 Goroutine 专门负责等待:

  1. 在该 Goroutine 中调用 wg.Wait(),这将阻塞直到所有任务的计数器归零。
  2. 当所有任务完成后,立即调用 close(c) 关闭 Channel。

注意:如果不关闭 Channel,后续的范围循环(range channel)会因为找不到数据且未收到结束信号而死锁或无限期阻塞。

// 独立携程监控等待组并关闭通道
go func() {
	wg.Wait()
	close(c)
}()

第4步:接收结果

最后,在主 Goroutine 中使用 range 循环读取 Channel。只要 Channel 未关闭且有数据流入,循环就会持续运行。一旦 Channel 关闭且数据取完,循环自然结束。

for msg := range c {
	fmt.Println(msg)
}

完整代码整合

将上述步骤结合,即得到完整的并发调用逻辑。每个子任务在处理完毕后,需通过 defer wg.Done() 确保计数器正确递减。

package main
import (
	"fmt"
	"net/http"
	"sync"
)
func main() {
	links := []string{
		"https://github.com/fabpot",
		"https://github.com/andrew",
		"https://github.com/taylorotwell",
		"https://github.com/egoist",
		"https://github.com/HugoGiraudel",
	}
	checkUrls(links)
}

func checkUrls(urls []string) {
	c := make(chan string)
	var wg sync.WaitGroup
	for _, link := range urls {
		wg.Add(1)   // 告诉 WaitGroup 有一个待处理的操作
		go checkUrl(link, c, &wg)
	}
	go func() {
		wg.Wait()   // 阻塞直到所有 wg.Done() 执行完毕
		close(c)    // 必须关闭通道,否则 for range 无法退出
	}() 
	// 循环读取结果,通道关闭且无数据后退出
	for msg := range c {
		fmt.Println(msg)
	}
}

func checkUrl(url string, c chan string, wg *sync.WaitGroup) {
	defer (*wg).Done()
	_, err := http.Get(url)
	if err != nil {
		c <- "Failed reaching:" + url
	} else {
		c <- "Success reaching:" + url
	}
}

以上就是 GoRoutines 高性能同时进行多个 API 调用实现的详细内容,更多关于 GoRoutines 多个 API 同时调用的资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。