go routine 不从通道中收集所有对象
go routine not collecting all objects from channel
我有一个 go-routine
用于将对象添加到通道中,然后我有 4 个 go-routines
用于处理通道的对象。处理只是将对象添加到数组。但有时,最终数组中缺少对象。所以我假设通道在某个时候停止收集对象。我有以下代码:
package main
import (
"log"
"sync"
)
func main() {
j := 0
for {
if j == 10 {
break
}
wg := sync.WaitGroup{}
months := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"}
hits := make(chan string)
i := 0
wg.Add(1)
go func() {
defer close(hits)
for {
if i == 25 {
wg.Done()
return
}
for _, month := range months {
hits <- month
}
i++
}
}()
temp := []string{}
for updateWorker := 1; updateWorker <= 4; updateWorker++ {
wg.Add(1)
go func() {
for hit := range hits {
temp = append(temp, hit)
}
wg.Done()
return
}()
}
wg.Wait()
log.Printf("length of temp %+v\n", len(temp))
j++
}
}
我正在使用 sync
库来同步例程。我正在循环相同的过程 10 次以测试输出是否一致。我期待这样的输出:
length of temp 175
它是 175,因为我发送 7 个月的字符串 25 次。
但是有时候输出不到175,不知道为什么。我对 go routines 有点初学者。那么有人可以帮我找到原因吗?谢谢。
问题是 updateWorker
goroutines 都从 hits
通道收集结果(到目前为止还不错),并且它们都将结果存储到 temp
局部变量 不同步。这不行。
必须同步访问来自多个 goroutine 的所有变量(其中至少有一个是写入)。
如果您 运行 它启用了竞争检测器,它会尖叫数据竞争 (go run -race app.go
)。
如果您将 updateWorker
goroutines 的数量减少到 1,它会立即产生有效结果,因为这样我们就消除了您应用程序的单一数据竞争源:
for updateWorker := 1; updateWorker <= 1; updateWorker++ {
// ...
}
如果你想保留多个 updateWorker
goroutines,它们对共享 temp
变量的访问必须同步。
与 sync.Mutex
:
var (
mu sync.Mutex
temp []string
)
for updateWorker := 1; updateWorker <= 4; updateWorker++ {
wg.Add(1)
go func() {
for hit := range hits {
mu.Lock()
temp = append(temp, hit)
mu.Unlock()
}
wg.Done()
return
}()
}
另请注意,在这个简单的示例中,您使用多个 updateWorker
goroutines 没有任何好处,添加上述同步(锁定)甚至会降低与仅使用其中一个 goroutines 相比的性能。
要正确分配工作和收集结果,请查看此答案:
我有一个 go-routine
用于将对象添加到通道中,然后我有 4 个 go-routines
用于处理通道的对象。处理只是将对象添加到数组。但有时,最终数组中缺少对象。所以我假设通道在某个时候停止收集对象。我有以下代码:
package main
import (
"log"
"sync"
)
func main() {
j := 0
for {
if j == 10 {
break
}
wg := sync.WaitGroup{}
months := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"}
hits := make(chan string)
i := 0
wg.Add(1)
go func() {
defer close(hits)
for {
if i == 25 {
wg.Done()
return
}
for _, month := range months {
hits <- month
}
i++
}
}()
temp := []string{}
for updateWorker := 1; updateWorker <= 4; updateWorker++ {
wg.Add(1)
go func() {
for hit := range hits {
temp = append(temp, hit)
}
wg.Done()
return
}()
}
wg.Wait()
log.Printf("length of temp %+v\n", len(temp))
j++
}
}
我正在使用 sync
库来同步例程。我正在循环相同的过程 10 次以测试输出是否一致。我期待这样的输出:
length of temp 175
它是 175,因为我发送 7 个月的字符串 25 次。 但是有时候输出不到175,不知道为什么。我对 go routines 有点初学者。那么有人可以帮我找到原因吗?谢谢。
问题是 updateWorker
goroutines 都从 hits
通道收集结果(到目前为止还不错),并且它们都将结果存储到 temp
局部变量 不同步。这不行。
必须同步访问来自多个 goroutine 的所有变量(其中至少有一个是写入)。
如果您 运行 它启用了竞争检测器,它会尖叫数据竞争 (go run -race app.go
)。
如果您将 updateWorker
goroutines 的数量减少到 1,它会立即产生有效结果,因为这样我们就消除了您应用程序的单一数据竞争源:
for updateWorker := 1; updateWorker <= 1; updateWorker++ {
// ...
}
如果你想保留多个 updateWorker
goroutines,它们对共享 temp
变量的访问必须同步。
与 sync.Mutex
:
var (
mu sync.Mutex
temp []string
)
for updateWorker := 1; updateWorker <= 4; updateWorker++ {
wg.Add(1)
go func() {
for hit := range hits {
mu.Lock()
temp = append(temp, hit)
mu.Unlock()
}
wg.Done()
return
}()
}
另请注意,在这个简单的示例中,您使用多个 updateWorker
goroutines 没有任何好处,添加上述同步(锁定)甚至会降低与仅使用其中一个 goroutines 相比的性能。
要正确分配工作和收集结果,请查看此答案: