英文:
Scanf in multiple goroutines giving unexpected results
问题
我只返回翻译好的部分,以下是你要翻译的内容:
我只是在尝试使用golang编程语言。我遇到了一个有趣的结果。这是我的代码。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var str1, str2 string
wg.Add(2)
go func() {
fmt.Scanf("%s", &str1)
wg.Done()
}()
go func() {
fmt.Scanf("%s", &str2)
wg.Done()
}()
wg.Wait()
fmt.Printf("%s %s\n", str1, str2)
}
我输入了以下内容。
beat
it
我期望的结果是
it beat
或者
beat it
但是我得到了
eat bit
请问有人可以帮我弄清楚为什么会这样吗?
英文:
I was simply experimenting in golang. I came across an interesting result. This is my code.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var str1, str2 string
wg.Add(2)
go func() {
fmt.Scanf("%s", &str1)
wg.Done()
}()
go func() {
fmt.Scanf("%s", &str2)
wg.Done()
}()
wg.Wait()
fmt.Printf("%s %s\n", str1, str2)
}
I gave the following input.
beat
it
I was expecting the result to be either
it beat
or
beat it
But I got.
eat bit
Can any one please help me figure out why it is so?
答案1
得分: 4
fmt.Scanf
不是一个原子操作。这是它的实现代码:http://golang.org/src/pkg/fmt/scan.go#L1115
没有信号量,也没有阻止两个并行执行的机制。因此,实际上这两个执行是并行的,由于没有缓冲,任何字节的读取都是一个IO操作,因此是Go调度器切换goroutine的理想时机。
英文:
fmt.Scanf
isn't an atomic operation. Here's the implementation : http://golang.org/src/pkg/fmt/scan.go#L1115
There's no semaphor, nothing preventing two parallel executions. So what happens is simply that the executions are really parallel, and as there's no buffering, any byte reading is an IO operation and thus a perfect time for the go scheduler to change goroutine.
答案2
得分: 4
问题在于你正在跨多个goroutine共享单个资源(stdin字节流)。
每个goroutine可能在不确定的时间点被创建。例如:
- 第一个goroutine读取所有的stdin,然后启动第二个goroutine。
- 第一个goroutine读取所有的stdin,然后启动第二个goroutine。
- 第一个goroutine在读取时被阻塞,然后启动第二个goroutine读取一个字符,然后重新启动第一个goroutine。
- ...以此类推...
在大多数情况下,只需要使用一个goroutine来访问线性资源(如字节流),并将一个通道连接到它,然后生成多个消费者来监听该通道。
例如:
package main
import (
"fmt"
"io"
"sync"
)
func main() {
var wg sync.WaitGroup
words := make(chan string, 10)
wg.Add(1)
go func() {
for {
var buff string
_, err := fmt.Scanf("%s", &buff)
if err != nil {
if err != io.EOF {
fmt.Println("Error:", err)
}
break
}
words <- buff
}
close(words)
wg.Done()
}()
// 多个消费者
for i := 0; i < 5; i += 1 {
go func() {
for word := range words {
fmt.Printf("%s\n", word)
}
}()
}
wg.Wait()
}
英文:
The problem is that you are sharing a single resource (the stdin byte stream) across multiple goroutines.
Each goroutine could be spawn at different non-deterministic times. i.e:
- first goroutine 1 read all stdin, then start goroutine 2
- first goroutine 2 read all stdin, then start goroutine 1
- first goroutine 1 block on read, then start goroutine 2 read one char and then restart goroutine 1
- ... and so on and on ...
In most cases is enough to use only one goroutine to access a linear resource as a byte stream and attach a channel to it and then spawn multiple consumers that listen to that channel.
For example:
package main
import (
"fmt"
"io"
"sync"
)
func main() {
var wg sync.WaitGroup
words := make(chan string, 10)
wg.Add(1)
go func() {
for {
var buff string
_, err := fmt.Scanf("%s", &buff)
if err != nil {
if err != io.EOF {
fmt.Println("Error: ", err)
}
break
}
words <- buff
}
close(words)
wg.Done()
}()
// Multiple consumers
for i := 0; i < 5; i += 1 {
go func() {
for word := range words {
fmt.Printf("%s\n", word)
}
}()
}
wg.Wait()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论