【问题标题】:Go lang, channel processing sequenceGo lang,通道处理序列
【发布时间】:2015-08-24 01:38:52
【问题描述】:

我正在通过'A tour of Go'学习Go lang,很难理解Go频道运行顺序,

package main

import "fmt"
import "time"

func sum(a []int, c chan int) {

    sum := 0
    for _, v := range a {
        time.Sleep(1000 * time.Millisecond)
        sum += v
    }
    c <- sum // send sum to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c // receive from c

    fmt.Println(x, y, x+y)
    fmt.Println("Print this first,")
}

如果在代码之上运行,我预计,

Print this first,
17 -5 12

因为,Go 例程以非阻塞方式运行,但实际上它会打印,

17 -5 12
Print this first,

我在网上找到的另一个例子,

package main

import "fmt"

type Data struct {
    i int
}

func func1(c chan *Data ) {
    fmt.Println("Called")
    for {
        var t *Data;
        t = <-c //receive
        t.i += 10 //increment
        c <- t   //send it back
    }
}

func main() {
    c := make(chan *Data)
    t := Data{10}
    go func1(c)
    println(t.i)
    c <- &t //send a pointer to our t
    i := <-c //receive the result
    println(i.i)
    println(t.i)
}

另外,我预计,它会先打印“Called”,但结果是

10
20
20
Called

我误会了什么?请帮助我了解 Go 例程和通道。

【问题讨论】:

  • 避免使用内置的print/println,因为实现与fmt不同。更好的是,如果您正在调试任何并发,请使用log,因为它会序列化并发调用,并且可以自动添加时间戳、文件名等。

标签: go


【解决方案1】:

在您的第一个示例中,x, y := &lt;-c, &lt;-c 将阻塞,直到它读取 c 两次,然后将值分配给 x, y。除了通道之外,您还有一个分配、一个打印语句,然后是另一个打印语句。这些都是同步的事情,并且会按照你声明它们的顺序发生。第二个 print 语句不可能先打印。

第二个是因为fmt.Println 写入 STDOUT,println 写入 STDERR。如果你是一致的(比如说在任何地方使用println),那么你会看到:

10
Called
20
20

这是因为在 main 中的第一个 println(t.i) 和 goroutine 中发生的 println("Called") 之间存在竞争。我猜GOMAXPROCS 设置为 1,这将始终如一地发生。将GOMAXPROCS 设置为NumCPU 时,我得到了混合的结果,有时看起来像上面,有时像这样:

10Called

20
20

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 2016-01-20
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多