ZetCode

Golang go 关键字

最后修改于 2025 年 5 月 7 日

本教程将介绍如何在 Go 中使用 go 关键字。我们将通过并发执行的实际示例涵盖 goroutine 的基础知识。

go 关键字启动一个新的 goroutine,这是一个由 Go 运行时管理的轻量级线程。Goroutines 能够实现函数的并发执行。

在 Go 中,go 用于函数调用之前,以并发方式执行它们。Goroutines 比操作系统线程更高效,并能实现高度并发的程序。

基本的 goroutine 示例

go 最简单的用法是创建一个函数调用的 goroutine。此示例演示了并发执行。
注意: 使用 time.Sleep 来等待 goroutines 只适用于简单的演示。在生产代码中,请使用 sync.WaitGroup 或 channels 进行proper synchronization。

basic_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("Hello from main")
}

sayHello 函数与主函数并发运行。我们使用 time.Sleep 来等待 goroutine 完成。

多个 goroutines

我们可以创建多个 goroutines 来并发执行函数。此示例显示了三个 goroutines 同时运行。

multiple_goroutines.go
package main

import (
    "fmt"
    "time"
)

func worker(id int) {
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    for i := 1; i <= 3; i++ {
        go worker(i)
    }
    
    time.Sleep(2 * time.Second)
    fmt.Println("All workers completed")
}

每个 worker goroutine 独立运行。主函数中的 sleep 确保所有 goroutines 在程序退出前完成。

匿名函数 goroutines

我们可以从匿名函数创建 goroutines。这对于快速并发操作很有用。

anonymous_goroutine.go
package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        fmt.Println("Running in goroutine")
    }()
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Running in main")
}

匿名函数与主函数并发执行。这种模式对于短暂的 goroutines 很常见。

带参数的 Goroutines

我们可以像普通函数调用一样为 goroutines 传递参数。此示例演示了参数传递。

goroutine_params.go
package main

import (
    "fmt"
    "time"
)

func printMessage(msg string) {
    fmt.Println(msg)
}

func main() {
    go printMessage("First message")
    go printMessage("Second message")
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Main message")
}

两个 goroutines 都正常接收它们的参数。由于调度原因,输出顺序在每次运行时可能会有所不同。

Goroutines 和共享内存

Goroutines 可以访问共享变量,但这需要同步来防止竞态条件。如果没有同步,多个 goroutines 同时修改共享变量可能导致不可预测的行为。一种确保线程安全的方法是使用 sync.Mutex

sync.Mutex(互斥锁)确保一次只有一个 goroutine 可以修改共享变量。在访问共享资源之前必须锁定 mutex,之后再解锁。这可以防止并发 goroutines 相互干扰,并确保结果的一致性。

shared_memory.go
package main

import (
    "fmt"
    "sync"
)

var counter int
var mutex sync.Mutex // Declare a mutex

func increment() {
    mutex.Lock() // Acquire the lock before modifying the shared variable
    counter++
    fmt.Println("Incremented to", counter)
    mutex.Unlock() // Release the lock after modification
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }

    wg.Wait() // Wait for all goroutines to complete
    fmt.Println("Final counter:", counter)
}

使用 sync.Mutex 可以防止竞态条件,确保一次只有一个 goroutine 可以修改 counter。关键特性包括:

如果没有 proper synchronization,多个 goroutines 可能会同时尝试修改 counter,导致意外的结果,例如不正确的最终值或不一致的增量。这被称为竞态条件,其结果取决于并发执行的不可预测的时序。

使用 sync.Mutex 可以确保对共享资源的更新是安全的,从而使代码在多次执行时都可靠且一致。

使用 WaitGroup 等待 Goroutines

sync.WaitGroup 提供了一种比 sleep 更好的等待 goroutines 的方法。此示例演示了 proper synchronization。

waitgroup.go
package main

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

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    // Simulate work
    time.Sleep(200 * time.Millisecond)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
    fmt.Println("All workers completed")
}

WaitGroup 跟踪 goroutine 的完成。Add 增加计数器,Done 减少计数器,Wait 阻塞直到计数器变为零。

带 channels 的 Goroutines

Channels 提供了 goroutines 之间的安全通信。此示例展示了 goroutines 发送和接收数据。

goroutine_channels.go
package main

import "fmt"

func produceMessages(ch chan<- string) {
    for i := 0; i < 5; i++ {
        ch <- fmt.Sprintf("Message %d", i)
    }
    close(ch)
}

func main() {
    ch := make(chan string)
    
    go produceMessages(ch)
    
    for msg := range ch {
        fmt.Println("Received:", msg)
    }
    
    fmt.Println("All messages received")
}

生产者 goroutine 通过 channel 发送消息。主 goroutine 接收它们,直到 channel 被关闭。

来源

Go 语言规范

本教程通过 goroutine 的创建和管理的实际示例,讲解了 Go 中的 go 关键字。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有 Golang 教程