多个goroutines中的Scanf会产生意外结果

时间:2014-05-28 09:11:41

标签: go

我只是在尝试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

任何人都可以帮我弄明白为什么会这样吗?

2 个答案:

答案 0 :(得分:4)

fmt.Scanf不是原子操作。以下是实施:http://golang.org/src/pkg/fmt/scan.go#L1115

没有信号,没有什么能阻止两个并行执行。所以发生的只是执行是真正的并行,并且由于没有缓冲,任何字节读取都是IO操作,因此是调度程序改变goroutine的最佳时间。

答案 1 :(得分:3)

问题在于您在多个goroutine之间共享单个资源(stdin字节流)。

每个goroutine都可以在不同的非确定性时间产卵。即:

  1. 第一个goroutine 1读取所有stdin,然后启动goroutine 2
  2. 第一个goroutine 2读取所有stdin,然后启动goroutine 1
  3. 首先读取goroutine 1块,然后启动goroutine 2读取一个char然后重新启动goroutine 1
  4. ......等等......
  5. 在大多数情况下,仅使用一个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()
        }()
        // Multiple consumers
        for i := 0; i < 5; i += 1 {
            go func() {
                for word := range words {
                    fmt.Printf("%s\n", word)
                }
            }()
        }
        wg.Wait()
    }