Go中的无缓冲通道

时间:2018-04-21 17:00:45

标签: go

以下是有关无缓冲通道的简单示例代码:

ch01 := make(chan string)

go func() {
    fmt.Println("We are in the sub goroutine")
    fmt.Println(<-ch01)
}()

fmt.Println("We are in the main goroutine")
ch01 <- "Hello"

我得到的结果:

We are in the main goroutine 
We are in the sub goroutine 
Hello

去游乐场: https://play.golang.org/p/rFWQbwXRzGw

根据我的理解,发送操作阻止了主goroutine,直到子goroutine在通道ch01上执行了接收操作。程序退出了。

在发送操作之后放置sub goroutine之后:

fmt.Println("We are in the main goroutine")
ch01 <- "Hello"

go func() {
    fmt.Println("We are in the sub goroutine")
    fmt.Println(<-ch01)
}()

发生了死锁:

We are in the main goroutine
fatal error: all goroutines are asleep - deadlock!
去操场 https://play.golang.org/p/DmRUiBG4UmZ

这次发生了什么?这是否意味着在ch01 <- "Hello"之后主要的goroutine立即被封锁,以便sub goroutine没有机会跑?如果是真的,我应该如何理解第一个代码示例的结果?(首先在主goroutine中,然后在sub goroutine中)。

4 个答案:

答案 0 :(得分:4)

发送时无缓冲的通道阻塞,直到接收器准备好读取。在您的第一个示例中,首先设置了一个阅读器,因此当发送时,它可以立即发送。

在你的第二个例子中,发送在接收器准备好之前发生,因此发送块和程序死锁。

您可以通过创建缓冲通道来修复第二个示例,但是有可能您无法看到goroutine的输出,因为程序可能会在刷新输出缓冲区之前退出(主goroutine)。在可以安排之前,goroutine甚至可能不会作为主要出口运行。

答案 1 :(得分:0)

首先,go-routines同时运行。在第一个例子中,sub-goroutine已经启动,但是在第二个例子中,当发送操作出现时,go-routine还没有开始。

逐行思考。

在第一个示例中,sub-goroutine已在主要运行例程中出现发送操作之前同时启动。结果,当发送操作发生时,已经存在接收器(sub-goroutine)。

如果你调整第一个例子,

package main

import (
    "fmt"
    "time"
)

func main() {
    ch01 := make(chan string)

    go func() {
        fmt.Println("We are in the sub goroutine")
        fmt.Println(<-ch01)
    }()

    // wait for start of sub-routine
    time.Sleep(time.Second * 2)

    fmt.Println("We are in the main goroutine")
    ch01 <- "Hello"

    // wait for the routine to receive and print the string
    time.Sleep(time.Second * 2)

}

输出

We are in the sub goroutine
We are in the main goroutine
Hello

所以,你可以看到sub-goroutine已经启动了。它正在等待在频道上接收。当主goroutine在通道中发送字符串时,sub-goroutine恢复并接收信号。

但是在第二个例子中,程序已经停留在主程序send operation中,并且子程序尚未启动并且不会启动,因为程序尚未获得该行。所以没有其他接收器接收信号。所以程序陷入僵局。

答案 2 :(得分:0)

对于无缓冲的通道,go例程将被阻止,直到没有人接收它为止。首先应该有一个go例程来从通道接收值,然后发送一个通道值。举例来说,当我们向通道发送一个值时,需要创建一个缓冲通道,以便将值保存到缓冲中,直到没有人接收它为止这样就可以了。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch01 := make(chan string, 10)
    ch01 <- "Hello"
    go func() {
        fmt.Println("We are in the sub goroutine")
        fmt.Println(<-ch01)
    }()

    fmt.Println("We are in the main goroutine")
    time.Sleep(1 * time.Second)
}

Playground

答案 3 :(得分:0)

  

这是否意味着在ch01&lt; - &#34; Hello&#34;主要的goroutine立刻   阻止,以便sub goroutine没有机会跑?如果是真的,   我应该如何理解第一个代码示例的结果?(起初   在主要的goroutine,然后在sub goroutine)。

这是真的。你理解写的东西。未指定产生的goroutine的评估顺序,只能使用同步工具(通道,互斥体)进行控制。第一个示例中的子goroutine也可以在另一个环境中首先打印()。它只是未指定