跳转到主要内容
模块 7 · 第 1 课(共 5)课程中的31/50~12 min
模块课程(1/5)

Goroutine:轻量级并行

goroutine 是 Go 管理的并发执行单元 运行时。您可以在函数前面使用 go 关键字启动一个 致电:

Go
go greet("Anna")         // chiamata a funzione esistente
go func() {              // funzione anonima
    fmt.Println("hi")
}()

go 语句立即返回:goroutine 开始于 并行,调用者继续。

为什么它们是“轻量级”

  • 初始堆栈 ~2KB(动态增长,如果需要,可达数百 MB)。
  • 通过运行时 (GOMAXPROCS) 多路复用到少量操作系统线程上。
  • 数千/数百万个 goroutine 是常规的;对于操作系统线程来说,这是不可能的。

从概念上讲,它是“并发的即发即弃”,但随着所有 用于协调的惯用工具(通道、sync.WaitGroup、 context)。

主要不等待

Go
func main() {
    go func() { fmt.Println("ciao dalla goroutine") }()
    // main esce subito: niente garanzia che la goroutine completi
}

当 main 返回时,进程终止:goroutines 不会得到 为了“完成他们的工作”,他们只是和 过程。需要显式同步。

同步:预览

您将在下一课程中看到的三个工具:

Go
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
    work()
    done <- struct{}{}
}()
<-done   // attendi

// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()

// 3) context: cancellazione/timeout propagati

“经典”闭环错误

在 Go 1.21 之前,这是一个常见的问题:

Go
for i := 0; i < 3; i++ {
    go func() { fmt.Println(i) }()  // stampa "3 3 3" su versioni vecchie
}

从 Go 1.22 for 变量是在每个 迭代,所以默认情况下修复了该错误。在较旧的代码库中,您将 仍然看到修复模式:

Go
for i := 0; i < 3; i++ {
    i := i           // shadow esplicito
    go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
    go func(n int) { fmt.Println(n) }(i)
}

Goroutine != 操作系统线程

方面协程操作系统线程
初始堆栈~2 KB1–8 MB(开始时分配)
创作纳秒微秒
日程安排Go 运行时(合作)内核(抢占式)
实用计数10万+几千

单个操作系统线程可以托管多个 goroutine。运行时移动 需要时跨线程的 goroutine。

尝试一下

锻炼#go.m7.l1.e1
尝试:0加载中...

将greet函数作为goroutine启动,并等待100ms,然后退出main。

正在加载编辑器...
显示提示

函数调用前面的“go”会启动 goroutine。

3 次尝试后可用的解决方案

锻炼#go.m7.l1.e2
尝试:0加载中...

启动一个匿名函数作为打印“hi”的 goroutine。

正在加载编辑器...
显示提示

匿名函数通过尾随 `()` 进行调用:`go func(){...}()`。

3 次尝试后可用的解决方案

Quiz#go.m7.l1.e3
准备好

如果 main 在 goroutine 完成之前退出会发生什么?

Go
func main() {
  go work()
  // main ritorna immediatamente
}
答案选项

回顾

  • go f() 同时启动 f();它立即返回。
  • Goroutine = ~2 KB 初始大小,多路复用到几个操作系统线程上。
  • main 不等待:您需要同步(通道/等待组/上下文)。
  • time.Sleep 只是说教性的,绝不用于生产中的同步。
  • 从 Go 1.22 开始,for 变量在每次迭代中都是“新鲜的”(不再有“3 3 3”错误)。