使用golang通道缺少数据
Lack of data using golang channel
我遇到了一个奇怪的问题。脚本如下。
package main
import (
"fmt"
"sync"
)
type Data struct {
data []int
}
func main() {
ws := 5
ch := make(chan *Data, ws)
var wg sync.WaitGroup
for i := 0; i < ws; i++ {
wg.Add(1)
go func(wg *sync.WaitGroup, ch chan *Data) {
defer wg.Done()
for {
char, ok := <-ch
if !ok {
return
}
fmt.Printf("Get: %d\n", len(char.data))
}
}(&wg, ch)
}
var d Data
ar := []int{1}
for i := 0; i < ws; i++ {
d.data = []int{}
for j := 0; j < 1000; j++ {
d.data = append(d.data, ar[0])
}
ch <- &d
// time.Sleep(time.Second / 1000) // When this line is moved, a number of data by put and get becomes same.
fmt.Printf("Put: %d\n", len(d.data))
}
close(ch)
wg.Wait()
}
这是运行,预计会出现以下结果。 "Put"和"Get"的数据个数相同。
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
但是,不可能每次都得到这个结果。结果如下。 "Put"和"Get"的数据个数每次都不一样
尝试 1
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
尝试 2
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 16
Put: 1000
Get: 0
尝试 3
Get: 1000
Put: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
虽然在我的电脑上,"Put"和"Get"的数据数量每次都不同,但在play.golang.org,两个数据的数量总是相同的。 https://play.golang.org/p/QFSuZmZk7d 为什么?
如果在脚本中使用time.Sleep(time.Second / 1000)
,则两个数据的数量相同。如果你知道这个问题,请你教我。非常感谢您的宝贵时间。
您观察到的是"data race"的例子。
当您同时访问同一块数据(其中至少一个是写入)时会发生这种情况。
你每次都引用同一个结构。接下来可能发生的是少数几种可能性之一:
它在你改变它之前在频道的另一边被读取("expected" 场景)
你在它被阅读之前就开始变异了。在这种情况下,接收方可以读取任意数量的 Data.data
个项目,从 0 到 1000,具体取决于读取发生的确切时间。
问题有多种解决方案:
您可以在每次迭代中创建 Data
的新实例。为此,只需将 var d Data
声明移动到循环体内即可。在这种情况下,每次迭代都会创建一个新结构,因此您可能不会错误地改变前一个结构。
您可以声明 Data
的通道(结构,而不是指向结构的指针):chan Data
。在这种情况下,每次将 Data
实例发送到通道时都会隐式复制它(因为 Go 中的所有内容都是按值传递的,在赋值时复制)。
package main
import (
"fmt"
"sync"
)
/*
信号量的考察,put 之后,必须等待 get 拿到之后才能推出循环
*/
type Data struct {
data []int
}
func main() {
ws := 5
ch := make(chan *Data, ws)
sem := make(chan bool)
var wg sync.WaitGroup
for i := 0; i < ws; i++ {
wg.Add(1)
go func(wg *sync.WaitGroup, ch chan *Data) {
defer wg.Done()
for {
char, ok := <-ch
if !ok {
return
}
fmt.Printf("Get: %d\n", len(char.data))
sem <- true
}
}(&wg, ch)
}
var d Data
ar := []int{1}
// ws = 5
for i := 0; i < ws; i++ {
d.data = []int{}
for j := 0; j < 1000; j++ {
d.data = append(d.data, ar[0])
}
ch <- &d
fmt.Printf("Put: %d\n", len(d.data))
<-sem // 一个信号量,必须等待 get 完成之后才能继续put
}
close(ch)
wg.Wait()
}
我遇到了一个奇怪的问题。脚本如下。
package main
import (
"fmt"
"sync"
)
type Data struct {
data []int
}
func main() {
ws := 5
ch := make(chan *Data, ws)
var wg sync.WaitGroup
for i := 0; i < ws; i++ {
wg.Add(1)
go func(wg *sync.WaitGroup, ch chan *Data) {
defer wg.Done()
for {
char, ok := <-ch
if !ok {
return
}
fmt.Printf("Get: %d\n", len(char.data))
}
}(&wg, ch)
}
var d Data
ar := []int{1}
for i := 0; i < ws; i++ {
d.data = []int{}
for j := 0; j < 1000; j++ {
d.data = append(d.data, ar[0])
}
ch <- &d
// time.Sleep(time.Second / 1000) // When this line is moved, a number of data by put and get becomes same.
fmt.Printf("Put: %d\n", len(d.data))
}
close(ch)
wg.Wait()
}
这是运行,预计会出现以下结果。 "Put"和"Get"的数据个数相同。
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
但是,不可能每次都得到这个结果。结果如下。 "Put"和"Get"的数据个数每次都不一样
尝试 1
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
尝试 2
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 16
Put: 1000
Get: 0
尝试 3
Get: 1000
Put: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
虽然在我的电脑上,"Put"和"Get"的数据数量每次都不同,但在play.golang.org,两个数据的数量总是相同的。 https://play.golang.org/p/QFSuZmZk7d 为什么?
如果在脚本中使用time.Sleep(time.Second / 1000)
,则两个数据的数量相同。如果你知道这个问题,请你教我。非常感谢您的宝贵时间。
您观察到的是"data race"的例子。
当您同时访问同一块数据(其中至少一个是写入)时会发生这种情况。
你每次都引用同一个结构。接下来可能发生的是少数几种可能性之一:
它在你改变它之前在频道的另一边被读取("expected" 场景)
你在它被阅读之前就开始变异了。在这种情况下,接收方可以读取任意数量的
Data.data
个项目,从 0 到 1000,具体取决于读取发生的确切时间。
问题有多种解决方案:
您可以在每次迭代中创建
Data
的新实例。为此,只需将var d Data
声明移动到循环体内即可。在这种情况下,每次迭代都会创建一个新结构,因此您可能不会错误地改变前一个结构。您可以声明
Data
的通道(结构,而不是指向结构的指针):chan Data
。在这种情况下,每次将Data
实例发送到通道时都会隐式复制它(因为 Go 中的所有内容都是按值传递的,在赋值时复制)。
package main
import (
"fmt"
"sync"
)
/*
信号量的考察,put 之后,必须等待 get 拿到之后才能推出循环
*/
type Data struct {
data []int
}
func main() {
ws := 5
ch := make(chan *Data, ws)
sem := make(chan bool)
var wg sync.WaitGroup
for i := 0; i < ws; i++ {
wg.Add(1)
go func(wg *sync.WaitGroup, ch chan *Data) {
defer wg.Done()
for {
char, ok := <-ch
if !ok {
return
}
fmt.Printf("Get: %d\n", len(char.data))
sem <- true
}
}(&wg, ch)
}
var d Data
ar := []int{1}
// ws = 5
for i := 0; i < ws; i++ {
d.data = []int{}
for j := 0; j < 1000; j++ {
d.data = append(d.data, ar[0])
}
ch <- &d
fmt.Printf("Put: %d\n", len(d.data))
<-sem // 一个信号量,必须等待 get 完成之后才能继续put
}
close(ch)
wg.Wait()
}