我目前正在学习Go Channels,我正在尝试这段代码。它创建 10 个 goroutine,每个 goroutine 向通道发送 1000 个 1。然后另一个 go 例程接收它并将其添加到计数器中。我还使用 WaitGroups 来确保 goroutine 在打印结果之前完成。
代码:
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
func main() {
var counter int
fmt.Println("\nWithout Channels -------")
for i := 0; i < 10; i++ {
go func() {
for j := 0; j < 1000; j++ {
// to simulate race condition
time.Sleep(time.Duration(1))
counter++
}
}()
}
runtime.Gosched()
fmt.Println("Expected counter: 10000, Actual counter:", counter)
fmt.Println("\nWith Channels -------")
for i := 0; i < 100; i++ {
WithChannels()
}
}
func WithChannels() {
var counter int
ch := make(chan int)
var wg sync.WaitGroup
// var wg2 sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
time.Sleep(time.Duration(1))
ch <- 1
}
}()
}
// wg2.Add(1)
go func() {
for {
increment, ok := <-ch
if !ok {
// channel closed, break
break
}
counter += increment
}
// wg2.Done()
}()
wg.Wait()
// wg2.Wait()
// time.Sleep(time.Duration(1) * time.Second)
fmt.Println("Expected counter: 10000, Actual counter:", counter)
close(ch)
}
输出:
Without Channels -------
Expected counter: 10000, Actual counter: 139
With Channels -------
Expected counter: 10000, Actual counter: 9999
Expected counter: 10000, Actual counter: 10000
Expected counter: 10000, Actual counter: 9999
Expected counter: 10000, Actual counter: 10000
Expected counter: 10000, Actual counter: 10000
Expected counter: 10000, Actual counter: 9999
...
我预计每次运行计数器时计数器都会为 10,000,但有时它似乎没有收到最后的值。我尝试为接收 goroutine 添加另一个 WaitGroup (在评论中看到),但这会导致死锁。
我在这里做错了什么吗?
您正在 Goroutine 中从通道接收数据,但从未等待它完成工作。您可以使用 Goroutine 来等待发送者完成并关闭通道,然后在主 Goroutine 中从通道接收数据。
func WithChannels() {
var counter int
ch := make(chan int)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
time.Sleep(time.Duration(1))
ch <- 1
}
}()
}
// Use a gorutine to wait for all the senders to complete and then close the channel.
go func() {
wg.Wait()
close(ch)
}()
// Receive from the channel until it's closed.
for {
increment, ok := <-ch
if !ok {
// channel closed, break
break
}
counter += increment
}
fmt.Println("Expected counter: 10000, Actual counter:", counter)
}