简体   繁体   English

从 goroutine 中的缓冲通道读取时的执行顺序

[英]Order of execution when reading from buffered channel in goroutine

I was playing around with concurrency and buffered channels and I hit upon the following problem that left me confused: https://play.golang.org/p/wir7wP2u-yf我在玩并发和缓冲通道时遇到了以下让我感到困惑的问题: https : //play.golang.org/p/wir7wP2u-yf

  1. Why exactly does the 'unload' of the channel (of size 3) inside the function echo happen with the 4 included?为什么函数 echo 内的通道(大小为 3)的“卸载”发生在 4 中?

  2. Why does the capacity of the channel c stay 0 after 5 is sent to the channel?为什么通道c的容量在5发送到通道后保持为0?

  3. Why is 10 not echoed?为什么 10 没有回显?

package main

import "fmt"

func echo(c chan int) {
    for num := range c {
        //fmt.Printf("length of channel c: %v\n",len(c))
        fmt.Println(num)
    }
    fmt.Println("Done iterating")
}

func main() {
    fmt.Println("main() started")
    c := make(chan int, 3)

    go echo(c)

    c <- 1
    fmt.Printf("After 1, capacity %v\n",len(c))
    c <- 2
    fmt.Printf("After 2, capacity %v\n",len(c))
    c <- 3
    fmt.Printf("After 3, capacity %v\n",len(c))
    c <- 4 // blocks here
    fmt.Printf("After 4, capacity %v\n",len(c))
    c <- 5
    fmt.Printf("After 5, capacity %v\n",len(c))
    c <- 6
    fmt.Printf("After 6, capacity %v\n",len(c))
    c <- 7
    fmt.Printf("After 7, capacity %v\n",len(c))
    c <- 8
    fmt.Printf("After 8, capacity %v\n",len(c))
    c <- 9
    fmt.Printf("After 9, capacity %v\n",len(c))
    c <- 10
    fmt.Printf("After 10\n")
    fmt.Println("main() stopped")
}

It entirely depends on OS scheduling.这完全取决于操作系统调度。 The results of the above code would not be always the same.上述代码的结果并不总是相同的。

main() started
After 1, capacity 1
After 2, capacity 2
After 3, capacity 3
1
2
3
4
After 4, capacity 0
After 5, capacity 0
After 6, capacity 1
After 7, capacity 2
After 8, capacity 3
5
6
7
8
9
After 9, capacity 0
After 10
main() stopped

This result might be the same as you saw in the playground.这个结果可能和你在操场上看到的一样。 But, it is unpredictable because we can't determine how OS schedule the execution order of goroutines.但是,这是不可预测的,因为我们无法确定操作系统如何调度 goroutine 的执行顺序。

If I run this code in my local machine, It will be different every time I execute it.如果我在本地机器上运行这段代码,每次执行都会有所不同。 See below:见下文:

First run第一次运行

main() started
After 1, capacity 1
After 2, capacity 1
After 3, capacity 2
1
2
3
4
After 4, capacity 3
After 5, capacity 0
5
After 6, capacity 0
After 7, capacity 1
After 8, capacity 2
After 9, capacity 3
6
7
8
9
10
After 10
main() stopped

Second run第二次运行

main() started
After 1, capacity 1
After 2, capacity 2
1
2
3
After 3, capacity 2
After 4, capacity 0
4
5
After 5, capacity 1
After 6, capacity 0
After 7, capacity 1
After 8, capacity 2
6
7
8
9
After 9, capacity 3
After 10
main() stopped
10

So answers to your questions would be所以你的问题的答案是

  1. Receiving the channel (for 4) just was executed before fmt.Printf in the main function.接收通道(for 4)只是在主函数中的fmt.Printf之前执行的。 It could be different every execution.每次执行都可能不同。 Try it on your local machine.在本地机器上试试。

  2. Similar to 1. It just read by range before len(c) .类似于 1。它只是在len(c)之前按range读取。

  3. The main goroutine exited before reading the 10 from the channel.主协程在从通道读取 10 之前退出。 In this case, you should wait for all items in the channel will be read using some techniques such as time sleeping, wait group of sync package or another channel.在这种情况下,您应该使用一些技术等待通道中的所有项目将被读取,例如时间睡眠、同步包的等待组或其他通道。

You can see how that program works by inserting the time.Sleep(time.Second) between the lines you want to inspect.您可以通过在要检查的行之间插入time.Sleep(time.Second)来查看该程序的工作方式。

For example, if you sleep for 1 second before fmt.Println("main() stopped") , you can see always the 10 echo because the main goroutine will be waiting for a second.例如,如果您在fmt.Println("main() stopped")之前休眠 1 秒,您总是可以看到10回声,因为主 goroutine 将等待一秒钟。 (1 second is big enough for reading an item from the channel) (1 秒足以从频道中读取项目)

package main

import "fmt"
import "time"

func echo(c chan int) {
    for num := range c {
        fmt.Println(num)
    }
    fmt.Println("Done iterating")
}

func main() {
    fmt.Println("main() started")
    c := make(chan int, 3)

    go echo(c)

    c <- 1
    fmt.Printf("After 1, capacity %v\n",len(c))
    c <- 2
    fmt.Printf("After 2, capacity %v\n",len(c))
    c <- 3
    fmt.Printf("After 3, capacity %v\n",len(c))
    c <- 4 // blocks here
    fmt.Printf("After 4, capacity %v\n",len(c))
    c <- 5
    fmt.Printf("After 5, capacity %v\n",len(c))
    c <- 6
    fmt.Printf("After 6, capacity %v\n",len(c))
    c <- 7
    fmt.Printf("After 7, capacity %v\n",len(c))
    c <- 8
    fmt.Printf("After 8, capacity %v\n",len(c))
    c <- 9
    fmt.Printf("After 9, capacity %v\n",len(c))
    c <- 10
    fmt.Printf("After 10\n")
    time.Sleep(time.Second)
    fmt.Println("main() stopped")
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM