英文:
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) // 当移动这行代码时,放入和取出的数据数量就会相同。
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上,两者的数量总是相同的。为什么会这样?如果在脚本中使用time.Sleep(time.Second / 1000)
,那么两者的数据数量就会相同。如果你了解这个问题,请告诉我。非常感谢你的时间。
英文:
I encountered a strange problem. The script is below.
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()
}
This is run, a following result is expected. The number of data for "Put" and "Get" is same.
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
But, this result cannot be got every time. The results are below. The number of data of "Put" and "Get" is different for every time.
Try 1
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Try 2
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 1000
Put: 1000
Get: 16
Put: 1000
Get: 0
Try 3
Get: 1000
Put: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
Put: 1000
Get: 1
Put: 1000
Get: 1000
ALthough on my PC, the number of data of "Put" and "Get" is different for every time, at play.golang.org, the number of both data is always same. https://play.golang.org/p/QFSuZmZk7d Why?
If time.Sleep(time.Second / 1000)
is used in the script, the number of both data becomes same. If you know about this problem, will you please teach me. Thank you so much for your time.
答案1
得分: 0
你观察到的是一个"数据竞争"的例子。
当你同时访问同一块数据时(至少其中一个是写操作),就会发生数据竞争。
你每次都将引用放在相同的结构体上。接下来可能发生的情况有几种可能性:
-
在你修改数据之前,它已经被另一端的通道读取("预期"的情况)。
-
在它被读取之前,你开始对其进行修改。在这种情况下,接收方可能会读取任意数量的
Data.data
项,从0到1000,具体取决于读取发生的时间。
解决这个问题有多种方法:
-
每次迭代都可以创建
Data
的新实例。为此,只需将var d Data
的声明放在循环体内部。这样,每次迭代都会创建一个新的结构体,因此你不会错误地修改先前的结构体。 -
你可以声明一个
Data
类型的通道(而不是指向结构体的指针):chan Data
。在这种情况下,每次将其发送到通道时,Data
实例会被隐式复制(因为Go中的所有内容都是按值传递的,在赋值时进行复制)。
英文:
What you observe is an example of "data race".
It happens when you concurrently access the same piece of data (with at least one of those being a write).
You put a reference to the same structure every time. And what may happen next is one of few possibilities:
-
it was read on the other side of the channel before you mutated it (the "expected" scenario)
-
you started mutating it before it was read. In this case the receiver may read any number of
Data.data
items, from 0 to 1000, depending on when exactly the read happened.
There are multiple solutions for the problem:
-
You may create the new instance of
Data
every iteration. For that simply move thevar d Data
declaration inside the loop body. In this case every iteration a new structure is created, so you may not mutate the previous one by mistake. -
You may declare channel of
Data
(the structures, not pointers to a structure):chan Data
. In this case theData
instance is implicitly copied every time you send it to the channel (since everything in Go is passed by value, copied on assignment).
答案2
得分: 0
package main
import (
"fmt"
"sync"
)
/*
Examining semaphores, after putting, you must wait for get to retrieve before exiting the loop
*/
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 // A semaphore, must wait for get to complete before continuing to put
}
close(ch)
wg.Wait()
}
英文:
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()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论