知用网
霓虹主题四 · 更硬核的阅读氛围

Go语言协程是什么 实用操作步骤与避坑指南

发布时间:2025-12-09 06:13:09 阅读:221 次

Go语言协程是什么

在写网络服务或者并发任务的时候,你可能经常听到“协程”这个词。尤其是在Go语言里,它几乎是绕不开的核心特性。那Go语言的协程到底是什么?简单来说,它是轻量级的执行单元,由Go运行时管理,能让你用很少的资源同时处理成千上万个任务。

比如你开了一家奶茶店,如果每个顾客来了都要一个店员全程盯着:点单、做奶茶、打包、收钱,那得雇多少人?现实中显然不会这么做。更高效的方式是把流程拆开,几个人分工协作,一个人可以同时照看多个环节。Go的协程就类似这个思路——它让一个操作系统线程能跑很多个“小任务”,这些小任务就是协程(Goroutine)。

怎么启动一个协程?

语法极其简单。你只需要在函数调用前加个 go 关键字:

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello from goroutine")
}

func main() {
    go sayHello()
    time.Sleep(100 * time.Millisecond) // 等一等,不然主程序可能直接退出
    fmt.Println("Main function ends")
}

上面这段代码中,go sayHello() 就启动了一个协程。主函数继续往下走的同时,sayHello 也在后台执行。注意那个 time.Sleep,它只是为了防止主程序太快结束,导致协程还没来得及打印就被杀掉。

协程为什么这么轻?

传统线程由操作系统调度,创建成本高,内存占用大,一般几千个就差不多到极限了。而Go的协程由Go runtime自己管,初始栈只有几KB,按需增长或收缩。你可以轻松起十万甚至百万个协程,只要你的程序逻辑需要。

举个例子,你要做个爬虫,抓一万个网页。如果用传统线程,系统可能直接卡死。但用Go协程,写起来就像这样:

func fetch(url string) {
    // 模拟网络请求
    time.Sleep(time.Second)
    fmt.Printf("Fetched %s\n", url)
}

func main() {
    urls := []string{"http://a.com", "http://b.com", "http://c.com"} // 假设有一万个
    for _, url := range urls {
        go fetch(url)
    }
    time.Sleep(2 * time.Second)
}

每一个 fetch 都在一个独立的协程里跑,互不干扰,资源消耗却很低。

协程之间怎么通信?

多个协程一起跑,总得交换数据吧?Go提倡“用通信来共享内存,而不是用共享内存来通信”。这话听着拗口,其实意思就是别乱抢变量,用 channel 来传消息。

还是拿奶茶店举例。如果多个员工都去改同一个订单状态,很容易出错。更好的方式是有个“消息板”,谁有更新就往板上贴条,其他人来看。channel 就是这块消息板。

func main() {
    ch := make(chan string)

    go func() {
        ch <- "奶茶做好了"
    }()

    msg := <-ch
    fmt.Println(msg)
}

这里,协程往 channel 发消息,主线程从里面收。两边不用碰同一块内存,也能安全协作。

Go语言的协程不是什么神秘技术,但它改变了我们写并发程序的方式。它不靠堆硬件,而是靠设计让程序更高效。当你看到一个Go服务扛住几万并发连接时,背后往往就是成千上万个安静运行的协程在干活。