Go 并发编程
Goroutine
Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理。
gopackage main import ( "fmt" "time" ) func sayHello() { fmt.Println("Hello from goroutine!") } func main() { // 启动一个 goroutine go sayHello() // 主 goroutine 继续执行 fmt.Println("Hello from main!") // 等待一下,让 goroutine 有机会执行 time.Sleep(time.Millisecond * 100) }
Channel
Channel 是 goroutine 之间通信的管道。
基本用法
gopackage main import "fmt" func main() { // 创建一个无缓冲 channel ch := make(chan string) go func() { ch <- "Hello from goroutine!" }() // 从 channel 接收数据 msg := <-ch fmt.Println(msg) // Hello from goroutine! }
缓冲 Channel
gopackage main import "fmt" func main() { // 创建一个缓冲大小为 2 的 channel ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) // 1 fmt.Println(<-ch) // 2 }
Select 语句
select 语句用于处理多个 channel 的发送和接收操作。
gopackage main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) go func() { time.Sleep(time.Second * 1) ch1 <- "from ch1" }() go func() { time.Sleep(time.Second * 2) ch2 <- "from ch2" }() select { case msg1 := <-ch1: fmt.Println("Received:", msg1) case msg2 := <-ch2: fmt.Println("Received:", msg2) case <-time.After(time.Second * 3): fmt.Println("Timeout!") } }
WaitGroup
WaitGroup 用于等待一组 goroutine 完成。
gopackage main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go worker(i, &wg) } wg.Wait() fmt.Println("All workers completed") }
常见并发模式
Worker Pool
gopackage main import ( "fmt" "sync" ) func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) { defer wg.Done() for job := range jobs { fmt.Printf("Worker %d processing job %d\n", id, job) results <- job * 2 } } func main() { const numJobs = 10 const numWorkers = 3 jobs := make(chan int, numJobs) results := make(chan int, numJobs) var wg sync.WaitGroup // 启动 workers for w := 1; w <= numWorkers; w++ { wg.Add(1) go worker(w, jobs, results, &wg) } // 发送 jobs for j := 1; j <= numJobs; j++ { jobs <- j } close(jobs) // 等待所有 workers 完成 go func() { wg.Wait() close(results) }() // 收集结果 for result := range results { fmt.Println("Result:", result) } }
常见面试题
1. 实现并发安全的计数器
gotype SafeCounter struct { mu sync.Mutex count int } func (c *SafeCounter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.count++ } func (c *SafeCounter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.count }
2. 实现带超时的并发请求
gofunc fetchWithTimeout(url string, timeout time.Duration) (string, error) { result := make(chan string, 1) errChan := make(chan error, 1) go func() { // 模拟网络请求 time.Sleep(time.Second * 2) result <- "response data" }() select { case res := <-result: return res, nil case err := <-errChan: return "", err case <-time.After(timeout): return "", fmt.Errorf("request timeout") } }
最佳实践
- 使用 defer 解锁:确保锁总是被释放
- 避免共享内存:优先使用 channel 通信
- 控制 goroutine 数量:避免创建过多 goroutine
- 处理 panic:在 goroutine 中使用 recover
- 使用 context 控制生命周期:特别是对于网络请求