Go 语言并发编程之 goroutine 入门
Go 语言以其简洁、高效和并发编程能力著称。在Go语言中,goroutine 是实现并发编程的核心机制。本文将围绕 goroutine 的概念、创建、使用和常见模式展开,帮助读者入门 Go 语言并发编程。
一、什么是 goroutine?
在Go语言中,goroutine 是轻量级的线程,是 Go 语言并发编程的基础。与传统的线程相比,goroutine 的创建和销毁更加高效,且占用资源更少。一个goroutine 可以看作是一个函数的执行实例,它可以在程序中并行执行。
二、创建 goroutine
在Go语言中,使用 `go` 关键字可以创建一个 goroutine。以下是一个简单的示例:
go
package main
import (
"fmt"
"time"
)
func main() {
go sayHello()
time.Sleep(1 time.Second) // 等待1秒,确保goroutine执行完毕
}
func sayHello() {
fmt.Println("Hello, World!")
}
在上面的代码中,`sayHello` 函数被封装在一个 goroutine 中执行。由于 `main` 函数中调用了 `time.Sleep`,程序会等待 1 秒,确保 `sayHello` 函数中的代码执行完毕。
三、goroutine 的生命周期
goroutine 的生命周期包括以下几个阶段:
1. 创建:使用 `go` 关键字创建一个 goroutine。
2. 运行:goroutine 开始执行,直到函数返回或遇到阻塞操作。
3. 等待:当 goroutine 遇到阻塞操作(如 I/O 操作、等待其他 goroutine 等)时,它会进入等待状态。
4. 结束:goroutine 执行完毕或被终止。
四、goroutine 的使用
goroutine 的使用非常灵活,以下是一些常见的使用场景:
1. 并行计算
go
package main
import (
"fmt"
"math/rand"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
result := calculate(id)
fmt.Printf("Result of %d: %d", id, result)
}(i)
}
wg.Wait()
}
func calculate(id int) int {
time.Sleep(time.Duration(rand.Intn(1000)) time.Millisecond)
return id 2
}
在上面的代码中,我们创建了 10 个 goroutine 来并行计算结果。
2. I/O 密集型任务
go
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
var wg sync.WaitGroup
urls := []string{
"http://www.google.com",
"http://www.bing.com",
"http://www.yahoo.com",
}
for _, url := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error fetching URL:", url, err)
return
}
fmt.Println("Fetched URL:", url, "Status Code:", resp.StatusCode)
}(url)
}
wg.Wait()
}
在上面的代码中,我们使用 goroutine 来并行获取多个网页的响应。
3. 等待多个 goroutine 完成
go
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Println("Goroutine", id, "is running")
time.Sleep(time.Duration(rand.Intn(1000)) time.Millisecond)
}(i)
}
wg.Wait()
fmt.Println("All goroutines have completed")
}
在上面的代码中,我们使用 `sync.WaitGroup` 来等待所有 goroutine 完成。
五、goroutine 的常见模式
1. WaitGroup
`sync.WaitGroup` 是一个同步原语,用于等待一组 goroutine 完成。在上面的示例中,我们已经使用了 `WaitGroup`。
2. Channel
Channel 是 Go 语言中用于 goroutine 间通信的机制。以下是一个使用 channel 的示例:
go
package main
import (
"fmt"
"sync"
)
func main() {
done := make(chan bool)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Println("Goroutine", id, "is running")
time.Sleep(time.Duration(rand.Intn(1000)) time.Millisecond)
done <- true
}(i)
}
wg.Wait()
close(done)
fmt.Println("All goroutines have completed")
for _ := range done {
fmt.Println("Goroutine has completed")
}
}
在上面的代码中,我们使用 channel `done` 来通知主 goroutine 所有子 goroutine 都已完成。
3. Context
`context` 包提供了一种取消信号和截止时间的传递机制,可以用于控制 goroutine 的执行。以下是一个使用 context 的示例:
go
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2time.Second)
defer cancel()
go func() {
fmt.Println("Goroutine is running")
time.Sleep(3 time.Second)
fmt.Println("Goroutine is done")
}()
select {
case <-ctx.Done():
fmt.Println("Operation timed out")
}
}
在上面的代码中,我们使用 `context.WithTimeout` 创建了一个带有超时的 context,如果子 goroutine 在 2 秒内没有完成,主 goroutine 将收到一个取消信号。
六、总结
goroutine 是 Go 语言并发编程的核心机制,它为开发者提供了高效、灵活的并发编程能力。通过本文的学习,读者应该对 goroutine 的概念、创建、使用和常见模式有了初步的了解。在实际开发中,合理地使用 goroutine 可以提高程序的并发性能,但也要注意避免常见的并发问题,如竞态条件、死锁等。
Comments NOTHING