Home > Article > Backend Development > Common mistakes and pitfalls of Golang coroutines
Common errors in Go coroutines include: coroutine leaks: excessive memory consumption due to incorrect release of resources; solution: use defer statement. Deadlock: Multiple coroutines wait in a loop; solution: avoid the loop waiting mode and use channel or sync.Mutex to coordinate access. Data race: Shared data is accessed by multiple coroutines at the same time; solution: use sync.Mutex or sync.WaitGroup to protect shared data. Timer cancellation: The timer is not canceled correctly after the coroutine is canceled; solution: use context.Context to propagate the cancellation signal.
In Go programming, coroutine (also known as goroutine) is a lightweight thread that can Helps develop concurrent applications. Although coroutines are very useful, they can also cause problems if used incorrectly. This guide will explore common mistakes and pitfalls of Go coroutines and provide best practices for avoiding them.
Problem: When a coroutine does not end as expected, it can cause a coroutine leak. This results in increased memory consumption and may eventually cause the application to crash.
Solution: Use the defer
statement to ensure that the resources in the coroutine are released correctly when the coroutine returns.
func example1() { defer wg.Done() // 确保等待组 wg 在例程返回时减 1 // ... 其他代码 }
Problem: A deadlock results when two or more coroutines wait for each other to complete. For example, in the following code, coroutine A waits for coroutine B to complete, and coroutine B waits for coroutine A to complete:
func example2() { ch1 := make(chan struct{}) ch2 := make(chan struct{}) go func() { <-ch1 // 等待协程 B ch2 <- struct{}{} // 向协程 B 发送信号 }() go func() { ch1 <- struct{}{} // 向协程 A 发送信号 <-ch2 // 等待协程 A }() }
Solution: Avoid running between multiple coroutines Create a loop wait pattern. Instead, consider using a channel or sync.Mutex
to coordinate access to shared resources.
Problem:When multiple coroutines access shared mutable data at the same time, a data race may result. This can lead to data corruption and unpredictable behavior.
Solution: Use a synchronization mechanism, such as sync.Mutex
or sync.WaitGroup
, to protect shared data from concurrent access.
var mu sync.Mutex func example3() { mu.Lock() // ... 访问共享数据 mu.Unlock() }
Problem:When the coroutine is canceled, the timer may not be canceled correctly. This can lead to unnecessary resource consumption and even cause the application to crash.
Solution: Use context.Context
to propagate cancellation signals and ensure that the timer starts in this context.
func example4(ctx context.Context) { timer := time.NewTimer(time.Second) defer timer.Stop() // 当 ctx 被取消时停止计时器 select { case <-timer.C: // 定时器已触发 case <-ctx.Done(): // 计时器已被取消 } }
The following is an example of using the above best practices to solve the coroutine leak problem:
func boundedGoroutinePool(n int) { var wg sync.WaitGroup ch := make(chan task, n) for i := 0; i < n; i++ { go func() { for task := range ch { wg.Add(1) go func() { defer wg.Done() task.Do() }() } }() } // ... 提交任务 close(ch) wg.Wait() }
By using a wait group (sync.WaitGroup
) to track running coroutines, we can ensure that the coroutine pool will not terminate before all submitted tasks are completed, thus avoiding coroutine leaks.
The above is the detailed content of Common mistakes and pitfalls of Golang coroutines. For more information, please follow other related articles on the PHP Chinese website!