DeepSeek 在 Go 语言中,通常不使用操作系统线程,而是使用 goroutine。goroutine 是 Go 运行时管理的轻量级“协程”,初始栈仅几 KB,创建销毁成本极低
在 Go 语言中,通常不使用操作系统线程,而是使用 goroutine。
goroutine 是 Go 运行时管理的轻量级“协程”,初始栈仅几 KB,创建销毁成本极低,可同时运行成千上万个。
- 启动 goroutine
在函数或方法调用前加上 go 关键字即可:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world") // 启动 goroutine
say("hello")
}
注意:main 函数返回时,所有 goroutine 都会立即终止,不会等待它们完成。
- 等待 goroutine 完成 —— sync.WaitGroup
使用 sync.WaitGroup 等待一组 goroutine 结束:
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 计数器减1
fmt.Printf("Worker %d starting\n", id)
// 模拟工作
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1) // 计数器加1
go worker(i, &wg)
}
wg.Wait() // 阻塞直到计数器归零
}
必须:在启动 goroutine 之前调用 wg.Add,并且将 wg 指针传给 goroutine。
- goroutine 间通信 —— channel
Go 的哲学:不要通过共享内存来通信,而应通过通信来共享内存。
channel 是 goroutine 间传递数据的管道。
无缓冲 channel(同步)
发送和接收操作会阻塞,直到另一端准备好:
ch := make(chan int)
go func() {
ch <- 42 // 发送,阻塞直到有人接收
}()
value := <-ch // 接收
fmt.Println(value)
有缓冲 channel(异步)
当缓冲未满时发送不阻塞,未空时接收不阻塞:
ch := make(chan string, 2)
ch <- "buffered"
ch <- "channel"
fmt.Println(<-ch)
fmt.Println(<-ch)
使用 range 和 close
发送方可以 close(ch) 关闭 channel,接收方用 for v := range ch 读取,直到 channel 关闭:
func fibonacci(n int, ch chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
ch <- x
x, y = y, x+y
}
close(ch)
}
func main() {
ch := make(chan int, 10)
go fibonacci(cap(ch), ch)
for i := range ch {
fmt.Println(i)
}
}
- select 多路复用
select 可以同时等待多个 channel 操作:
select {
case v1 := <-ch1:
fmt.Println("received", v1)
case v2 := <-ch2:
fmt.Println("received", v2)
case ch3 <- 23:
fmt.Println("sent on ch3")
default:
fmt.Println("no communication")
}
常用于超时控制、非阻塞收发。
- 注意事项
· 不要泄漏 goroutine:确保 goroutine 最终能退出(比如通过关闭 channel 或使用 context 取消)。
· panic 处理:每个 goroutine 内部应该 recover,防止整个程序崩溃。
· GOMAXPROCS:默认使用所有 CPU 核,可调用 runtime.GOMAXPROCS(n) 调整,但一般不需要。
· 互斥锁:若必须共享内存,用 sync.Mutex 保护临界区。
- 简单示例:并发抓取 URL
package main
import (
"fmt"
"net/http"
"sync"
"time"
)
func fetchURL(url string, wg *sync.WaitGroup) {
defer wg.Done()
start := time.Now()
resp, err := http.Get(url)
if err != nil {
fmt.Printf("%s -> error: %v\n", url, err)
return
}
defer resp.Body.Close()
fmt.Printf("%s -> %d, took %v\n", url, resp.StatusCode, time.Since(start))
}
func main() {
urls := []string{
"https://golang.org",
"https://google.com",
"https://github.com",
}
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go fetchURL(url, &wg)
}
wg.Wait()
}
掌握 goroutine + channel + WaitGroup,足以应对绝大多数并发场景。
更多推荐

所有评论(0)