Go语言并发编程实战 前言 Go语言天生为并发而生,其简洁的并发模型让开发者可以轻松编写高性能的并发程序。本文将深入探讨Go并发编程的核心概念、常见模式和最佳实践,帮助你写出更高效、更优雅的并发代码。
一、Go并发基础 1.1 Goroutine入门 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "fmt" "time" ) func sayHello (name string ) { for i := 0 ; i < 3 ; i++ { fmt.Printf("Hello, %s! (count: %d)\n" , name, i) time.Sleep(100 * time.Millisecond) } } func main () { go sayHello("Alice" ) go sayHello("Bob" ) go func () { fmt.Println("Anonymous goroutine" ) }() time.Sleep(500 * time.Millisecond) fmt.Println("Main function done" ) }
Goroutine特点 :
轻量级:初始栈只有2KB
调度高效:M:N调度模型
通信方便:通过channel通信
1.2 Channel基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ch := make (chan int ) buffered := make (chan int , 10 ) ch <- 42 value := <-ch value, ok := <-ch close (ch)func send (ch chan <- int ) { ch <- 1 } func receive (ch <-chan int ) { val := <-ch }
Channel示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func sum (numbers []int , resultCh chan int ) { sum := 0 for _, num := range numbers { sum += num } resultCh <- sum } func main () { numbers := []int {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 } resultCh := make (chan int ) go sum(numbers[:len (numbers)/2 ], resultCh) go sum(numbers[len (numbers)/2 :], resultCh) sum1, sum2 := <-resultCh, <-resultCh fmt.Printf("Total: %d\n" , sum1+sum2) }
二、并发同步 2.1 WaitGroup 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 done" ) }
2.2 Mutex互斥锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 type SafeCounter struct { mu sync.Mutex count map [string ]int } func (c *SafeCounter) Inc(key string ) { c.mu.Lock() defer c.mu.Unlock() c.count[key]++ } func (c *SafeCounter) Value(key string ) int { c.mu.Lock() defer c.mu.Unlock() return c.count[key] } func main () { counter := SafeCounter{count: make (map [string ]int )} var wg sync.WaitGroup for i := 0 ; i < 1000 ; i++ { wg.Add(1 ) go func () { defer wg.Done() counter.Inc("key" ) }() } wg.Wait() fmt.Println(counter.Value("key" )) }
2.3 RWMutex读写锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 type SafeMap struct { mu sync.RWMutex data map [string ]string } func (m *SafeMap) Get(key string ) (string , bool ) { m.mu.RLock() defer m.mu.RUnlock() val, ok := m.data[key] return val, ok } func (m *SafeMap) Set(key, value string ) { m.mu.Lock() defer m.mu.Unlock() m.data[key] = value } func main () { sm := SafeMap{data: make (map [string ]string )} for i := 0 ; i < 10 ; i++ { go func (id int ) { val, _ := sm.Get("key" ) fmt.Printf("Reader %d: %s\n" , id, val) }(i) } go func () { sm.Set("key" , "value" ) }() time.Sleep(time.Second) }
2.4 Once确保只执行一次 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type Config struct { once sync.Once data string } func (c *Config) Load() { c.once.Do(func () { fmt.Println("Loading config..." ) time.Sleep(time.Second) c.data = "config loaded" }) } func main () { config := &Config{} for i := 0 ; i < 5 ; i++ { go config.Load() } time.Sleep(2 * time.Second) fmt.Println(config.data) }
三、Channel高级用法 3.1 Select多路复用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func main () { ch1 := make (chan string ) ch2 := make (chan string ) go func () { time.Sleep(1 * time.Second) ch1 <- "from ch1" }() go func () { time.Sleep(2 * time.Second) ch2 <- "from ch2" }() for i := 0 ; i < 2 ; i++ { select { case msg1 := <-ch1: fmt.Println(msg1) case msg2 := <-ch2: fmt.Println(msg2) case <-time.After(3 * time.Second): fmt.Println("timeout" ) return } } }
3.2 超时控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func doWork () <-chan string { result := make (chan string ) go func () { time.Sleep(2 * time.Second) result <- "work done" }() return result } func main () { result := doWork() select { case res := <-result: fmt.Println(res) case <-time.After(1 * time.Second): fmt.Println("timeout after 1 second" ) } }
3.3 Fan-out/Fan-in模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 func producer (numbers ...int ) <-chan int { out := make (chan int ) go func () { defer close (out) for _, n := range numbers { out <- n } }() return out } func square (in <-chan int ) <-chan int { out := make (chan int ) go func () { defer close (out) for n := range in { out <- n * n } }() return out } func merge (channels ...<-chan int ) <-chan int { out := make (chan int ) var wg sync.WaitGroup wg.Add(len (channels)) for _, ch := range channels { go func (c <-chan int ) { defer wg.Done() for n := range c { out <- n } }(ch) } go func () { wg.Wait() close (out) }() return out } func main () { in := producer(1 , 2 , 3 , 4 , 5 ) c1 := square(in) c2 := square(in) c3 := square(in) for n := range merge(c1, c2, c3) { fmt.Println(n) } }
四、并发模式 4.1 生产者-消费者模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 type Task struct { ID int Data string } func producer (tasks chan <- Task, count int ) { defer close (tasks) for i := 1 ; i <= count; i++ { task := Task{ ID: i, Data: fmt.Sprintf("Task-%d" , i), } tasks <- task fmt.Printf("Produced: %v\n" , task) time.Sleep(100 * time.Millisecond) } } func consumer (id int , tasks <-chan Task, wg *sync.WaitGroup) { defer wg.Done() for task := range tasks { fmt.Printf("Consumer %d processing %v\n" , id, task) time.Sleep(200 * time.Millisecond) } } func main () { tasks := make (chan Task, 10 ) go producer(tasks, 20 ) var wg sync.WaitGroup for i := 1 ; i <= 3 ; i++ { wg.Add(1 ) go consumer(i, tasks, &wg) } wg.Wait() fmt.Println("All tasks processed" ) }
4.2 Worker Pool模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 type Job struct { ID int Number int } type Result struct { Job Job Sum int } func worker (id int , jobs <-chan Job, results chan <- Result) { for job := range jobs { fmt.Printf("Worker %d processing job %d\n" , id, job.ID) sum := 0 for i := 1 ; i <= job.Number; i++ { sum += i } results <- Result{Job: job, Sum: sum} } } func main () { numJobs := 100 numWorkers := 5 jobs := make (chan Job, numJobs) results := make (chan Result, numJobs) for w := 1 ; w <= numWorkers; w++ { go worker(w, jobs, results) } for j := 1 ; j <= numJobs; j++ { jobs <- Job{ID: j, Number: j * 10 } } close (jobs) for i := 1 ; i <= numJobs; i++ { result := <-results fmt.Printf("Job %d result: %d\n" , result.Job.ID, result.Sum) } }
4.3 Pipeline模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 func generate (nums ...int ) <-chan int { out := make (chan int ) go func () { defer close (out) for _, n := range nums { out <- n } }() return out } func square (in <-chan int ) <-chan int { out := make (chan int ) go func () { defer close (out) for n := range in { out <- n * n } }() return out } func filter (in <-chan int , threshold int ) <-chan int { out := make (chan int ) go func () { defer close (out) for n := range in { if n > threshold { out <- n } } }() return out } func main () { numbers := generate(1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) squared := square(numbers) filtered := filter(squared, 50 ) for n := range filtered { fmt.Println(n) } }
五、Context上下文管理 5.1 Context基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import "context" func worker (ctx context.Context, id int ) { for { select { case <-ctx.Done(): fmt.Printf("Worker %d stopping: %v\n" , id, ctx.Err()) return default : fmt.Printf("Worker %d working\n" , id) time.Sleep(500 * time.Millisecond) } } } func main () { ctx, cancel := context.WithCancel(context.Background()) for i := 1 ; i <= 3 ; i++ { go worker(ctx, i) } time.Sleep(2 * time.Second) cancel() time.Sleep(time.Second) }
5.2 超时Context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func doRequest (ctx context.Context) error { result := make (chan string , 1 ) go func () { time.Sleep(2 * time.Second) result <- "success" }() select { case <-ctx.Done(): return ctx.Err() case res := <-result: fmt.Println(res) return nil } } func main () { ctx, cancel := context.WithTimeout(context.Background(), 1 *time.Second) defer cancel() if err := doRequest(ctx); err != nil { fmt.Printf("Request failed: %v\n" , err) } }
5.3 传递值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 type key string func worker (ctx context.Context) { if userID, ok := ctx.Value(key("userID" )).(string ); ok { fmt.Printf("Processing for user: %s\n" , userID) } if requestID, ok := ctx.Value(key("requestID" )).(string ); ok { fmt.Printf("Request ID: %s\n" , requestID) } } func main () { ctx := context.Background() ctx = context.WithValue(ctx, key("userID" ), "user123" ) ctx = context.WithValue(ctx, key("requestID" ), "req456" ) go worker(ctx) time.Sleep(time.Second) }
六、并发安全 6.1 数据竞争检测 1 2 3 4 go run -race main.go go build -race go test -race
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var counter int func increment () { counter++ } func main () { for i := 0 ; i < 1000 ; i++ { go increment() } time.Sleep(time.Second) fmt.Println(counter) }
6.2 原子操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import "sync/atomic" var counter int64 func increment () { atomic.AddInt64(&counter, 1 ) } func main () { for i := 0 ; i < 1000 ; i++ { go increment() } time.Sleep(time.Second) fmt.Println(atomic.LoadInt64(&counter)) }
七、性能优化 7.1 避免Goroutine泄露 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func leak () { ch := make (chan int ) go func () { val := <-ch fmt.Println(val) }() } func noLeak () { ch := make (chan int ) done := make (chan bool ) go func () { select { case val := <-ch: fmt.Println(val) case <-done: return } }() close (done) }
7.2 控制并发数量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func limitConcurrency (maxGoroutines int ) { sem := make (chan struct {}, maxGoroutines) tasks := make ([]int , 100 ) for i := range tasks { tasks[i] = i } var wg sync.WaitGroup for _, task := range tasks { wg.Add(1 ) sem <- struct {}{} go func (t int ) { defer wg.Done() defer func () { <-sem }() fmt.Printf("Processing task %d\n" , t) time.Sleep(100 * time.Millisecond) }(task) } wg.Wait() }
八、实战案例 8.1 并发网络爬虫 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 type Fetcher interface { Fetch(url string ) (urls []string , err error ) } func crawl (url string , depth int , fetcher Fetcher, visited map [string ]bool , mu *sync.Mutex, wg *sync.WaitGroup) { defer wg.Done() if depth <= 0 { return } mu.Lock() if visited[url] { mu.Unlock() return } visited[url] = true mu.Unlock() urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s\n" , url) for _, u := range urls { wg.Add(1 ) go crawl(u, depth-1 , fetcher, visited, mu, wg) } } func Crawl (url string , depth int , fetcher Fetcher) { visited := make (map [string ]bool ) var mu sync.Mutex var wg sync.WaitGroup wg.Add(1 ) go crawl(url, depth, fetcher, visited, &mu, &wg) wg.Wait() }
九、最佳实践 核心原则
不要通过共享内存来通信,而要通过通信来共享内存
总是使用defer关闭channel和释放锁
避免goroutine泄露,确保所有goroutine都能退出
使用context管理goroutine生命周期
控制并发数量,避免创建过多goroutine
常见陷阱 1 2 3 4 5 6 7 8 9 10 11 12 13 for i := 0 ; i < 10 ; i++ { go func () { fmt.Println(i) }() } for i := 0 ; i < 10 ; i++ { go func (i int ) { fmt.Println(i) }(i) }
十、总结 Go的并发模型让并发编程变得简单而优雅:
核心概念
Goroutine :轻量级协程
Channel :通信和同步
Select :多路复用
Sync包 :低级同步原语
并发模式
Pipeline
Fan-out/Fan-in
Worker Pool
生产者-消费者
掌握Go并发编程,让你的程序飞起来!
参考资料