将sync.WaitGroup与外部函数结合使用的最佳实践
在处理Go中的并发时,有效利用sync.WaitGroup至关重要。本文解决了将等待组作为参数传递给外部函数时出现的常见问题。
问题:
考虑以下代码:
<code class="go">package main import ( "fmt" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go Print(ch, wg) // go func(){ for i := 1; i <= 11; i++ { ch <- i } close(ch) defer wg.Done() }() wg.Wait() //deadlock here } // Print prints all numbers sent on the channel. // The function returns when the channel is closed. func Print(ch <-chan int, wg sync.WaitGroup) { for n := range ch { // reads from channel until it's closed fmt.Println(n) } defer wg.Done() }</code>
这段代码中,在指定行发生死锁,导致程序只打印从1到10,而不是到达11。该错误源于将sync.WaitGroup的副本传递给Print方法,这阻碍了对其 Done 方法的预期调用。
解决方案 1:
要解决此问题,请将指针传递给等待组:
<code class="go">package main import ( "fmt" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go Print(ch, &wg) go func() { for i := 1; i <= 11; i++ { ch <- i } close(ch) defer wg.Done() }() wg.Wait() //deadlock here } func Print(ch <-chan int, wg *sync.WaitGroup) { for n := range ch { // reads from channel until it's closed fmt.Println(n) } defer wg.Done() }</code>
传递 wg 的地址可以确保 Print 方法调用 main 函数中等待的等待组上的 Done 方法。
解决方案 2:简化的 Print 方法
或者,可以通过删除 WaitGroup 参数来简化 Print 方法,因为它不需要了解任何等待操作:
<code class="go">package main import ( "fmt" ) func main() { ch := make(chan int) go func() { for i := 1; i <= 11; i++ { ch <- i } close(ch) }() for n := range ch { // reads from channel until it's closed fmt.Println(n) } } </code>
在这种情况下,主 goroutine 直接接收通道并打印它的值不涉及等待组。这种方法保留了所需的功能,并消除了 Print 方法中对 WaitGroup 管理的需要。
结论:
当将sync.WaitGroup 作为参数传递给外部函数时,确保函数接收到正在等待的等待组的正确引用至关重要。重构函数以直接处理等待组或传递指针到等待组可以有效防止死锁错误并确保正确的并发控制。
以上是在 Go 中使用带有外部函数的sync.WaitGroup时如何避免死锁?的详细内容。更多信息请关注PHP中文网其他相关文章!