Go 语言入门:用一个并发小工具理解 goroutine 与 channel

举报
yd_268428615 发表于 2026/11/14 06:08:13 2026/11/14
【摘要】 本文以一个并发批量检查网址状态的小工具为例,介绍 Go 语言的模块管理、goroutine 与 channel 的基本用法,并给出完整可运行代码和四个常见坑,帮助初学者快速写出第一个可靠的 Go 并发程序。

刚接触 Go 的开发者常会问:这门语言到底“简单”在哪里?答案不在语法糖的多少,而在它把并发做成了语言的一部分。本文用一个真实可跑的小工具——并发批量检查网址状态——带你走完 Go 入门最关键的几步:环境与模块管理、基本语法、goroutine 与 channel、错误处理。读完之后,你能独立写出并运行这个小工具,也能把同样的思路用到自己的日常脚本里。

一、准备工作:安装与第一个模块

先到 Go 官网下载对应系统的安装包,安装完成后在终端执行 go version,能看到版本号就说明环境就绪。Go 从 1.11 起用模块(module)管理依赖,写小工具不需要复杂的工程结构,新建一个空目录并执行:

mkdir urlcheck && cd urlcheck
go mod init urlcheck

go mod init 会生成 go.mod 文件,记录模块名和 Go 版本。之后所有代码都放在这个目录下,go run . 就能直接运行,go build -o urlcheck . 则会编译出可执行文件。养成一个习惯:任何一个新想法,先建模块、再写代码,依赖和版本从一开始就是清晰的。

二、从串行到并发:为什么需要 goroutine

假设要检查十个网址是否可达,最直觉的写法是 for 循环逐个请求:

for _, u := range urls {
    resp, err := http.Get(u)
    // 处理结果
}

串行的问题很直观:每个请求都要等上一个完成,十个网址各耗时一秒,总耗时就是十秒。而这些请求之间互不依赖,完全可以同时进行。Go 的解法是 goroutine——由 Go 运行时调度的轻量级线程,启动它只需要一个 go 关键字:

go check(url)

一个 goroutine 的初始栈只有几 KB,创建成千上万个的成本也远低于操作系统线程。主函数 main 结束时程序会直接退出,所以必须有办法等所有 goroutine 跑完,这就要用到 sync.WaitGroup。

三、用 channel 收集结果

并发跑起来之后,下一个问题是结果怎么汇总。直接让多个 goroutine 写同一个变量会有数据竞争,Go 推荐的做法是 channel:goroutine 把结果“发”进通道,主函数再从通道里“收”。

type Result struct {
    URL    string
    Status int
    Err    error
}

results := make(chan Result, len(urls))

带缓冲的 channel 可以让发送方不必等待接收方,配合 WaitGroup 就能写出清晰的模式:启动 N 个 goroutine 做事,每个完成时调用 wg.Done(),另起一个 goroutine 等 wg.Wait() 返回后关闭 channel,主函数用 for r := range results 逐条消费。这种“生产者—通道—消费者”的结构,是 Go 并发程序最常见的骨架,学会它就等于掌握了 Go 并发的一半。

四、完整代码:并发网址检查器

把上面的思路拼起来,完整程序不到一百行:

package main

import (
    "fmt"
    "net/http"
    "sync"
    "time"
)

type Result struct {
    URL    string
    Status int
    Err    error
}

func check(url string, wg *sync.WaitGroup, ch chan<- Result) {
    defer wg.Done()
    client := &http.Client{Timeout: 5 * time.Second}
    resp, err := client.Get(url)
    if err != nil {
        ch <- Result{URL: url, Err: err}
        return
    }
    defer resp.Body.Close()
    ch <- Result{URL: url, Status: resp.StatusCode}
}

func main() {
    urls := []string{
        "https://www.huaweicloud.com",
        "https://developer.huaweicloud.com",
        "https://go.dev",
    }
    ch := make(chan Result, len(urls))
    var wg sync.WaitGroup
    for _, u := range urls {
        wg.Add(1)
        go check(u, &wg, ch)
    }
    go func() {
        wg.Wait()
        close(ch)
    }()
    for r := range ch {
        if r.Err != nil {
            fmt.Printf("%s 失败:%v\n", r.URL, r.Err)
        } else {
            fmt.Printf("%s 状态码:%d\n", r.URL, r.Status)
        }
    }
}

运行 go run .,三个网址的检查几乎同时完成。把列表换成你真正关心的服务地址,它就是一个能用的健康检查小工具。

五、写并发代码最容易踩的四个坑

第一,忘记设置超时。没有 Timeout 的 HTTP 客户端可能一直挂起,整个程序被一个慢接口拖死,超时是并发程序的必需品。第二,无限制地启动 goroutine。网址从三个变成三万个时,直接起三万个 goroutine 会耗尽连接和内存,应该用固定数量的 worker 加任务队列来限流。第三,忘记关闭 channel 或者在错误分支里漏发结果,主函数的 range 会永远等下去。第四,在 goroutine 里共享变量而不加锁或不用 channel,go run -race 可以帮你发现这类数据竞争,开发阶段值得常开。

六、接下来学什么

跑通这个小工具之后,建议按这个顺序深入:先学 context,用它实现可取消的超时控制;再学标准库的 net/http 服务端写法,自己写一个返回检查结果的接口;然后学 testing,给 check 函数写单元测试;最后了解 errgroup,它能把 WaitGroup 和错误收集封装得更简洁。这条路上的每一步都有现成的小工具可以练手,边做边学比只看语法书快得多。

Go 的入门门槛确实不高,但“写出能跑的并发程序”和“写出可靠的并发程序”之间隔着超时、限流、错误处理这些工程细节。从一个几十行的小工具开始,把这些细节逐个补齐,就是最扎实的 Go 入门方式。现在就动手试试吧。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。