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。
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 同时运行。
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。这对于快速并发操作很有用。
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 传递参数。此示例演示了参数传递。
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 相互干扰,并确保结果的一致性。
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
。关键特性包括:
- 使用
mutex.Lock
和mutex.Unlock
在修改counter
时确保互斥。 - 添加
sync.WaitGroup
确保程序在打印最终值之前等待所有 goroutines 完成执行。 - 使用
defer wg.Done
确保每个 goroutine 都发出其完成信号。
如果没有 proper synchronization,多个 goroutines 可能会同时尝试修改 counter
,导致意外的结果,例如不正确的最终值或不一致的增量。这被称为竞态条件,其结果取决于并发执行的不可预测的时序。
使用 sync.Mutex
可以确保对共享资源的更新是安全的,从而使代码在多次执行时都可靠且一致。
使用 WaitGroup 等待 Goroutines
sync.WaitGroup
提供了一种比 sleep 更好的等待 goroutines 的方法。此示例演示了 proper synchronization。
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 发送和接收数据。
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 被关闭。
来源
本教程通过 goroutine 的创建和管理的实际示例,讲解了 Go 中的 go
关键字。