尝试运行以下代码(生产者和消费者)以了解golang中的goroutine和通道(从下面的代码片段中删除了包和导入):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var done = make(chan bool)
var msgs = make(chan int)

func produce() {
    for i := 0; i < 10; i++ {
            msgs <- i
    }
    fmt.Println("Before closing channel")
    close(msgs)
    fmt.Println("Before passing true to done")
    done <- true
}

func consume() {
    for {
            msg := <-msgs
            time.Sleep(100 * time.Millisecond)
            fmt.Println("Consumer:", msg)
    }
}

func main() {
    go produce()
    go consume()
    <-done
    fmt.Println("After calling DONE")
}

源代码来自:http://www.golangpatterns.info/concurrency/producer-consumer

下面是我运行代码时的输出

1
2
3
4
5
6
7
8
9
10
11
12
Consumer:  0
Consumer:  1
Consumer:  2
Consumer:  3
Consumer:  4
Consumer:  5
Consumer:  6
Consumer:  7
Consumer:  8
Before closing channel
Before passing true to done
After calling DONE

根据我对goroutine和渠道的了解:
当我们使用go关键字从main()调用produce()和consume()时,运行时将踢出2个goroutines(Java世界中的某种线程,但不是实际的OS线程),并且main()goroutine出现并在" < -完成"。 现在在Produce()内部-循环从0到9,并且在循环内部msgs通道一次接收由int()并行消耗的int(0至9)1;但是Produce对此一无所知,只是不断循环0到9。

问题:假设我的理解是正确的。一次,fo??r循环完成了,为什么Produce()中的下一个printLine无法打印,以及为什么msgs通道没有关闭?为什么goroutine在Producer()内部停止,直到消费者耗尽了所有msg?

  • 生产者中的print语句确实得到打印,并且for循环退出后,msgs通道立即关闭。 我不明白你在问什么。
  • 那么,为什么在输出中,在"消费者:8"之后(即在所有味精消耗完之后)打印打印语句"关闭通道之前"和"传递完成之前"?
  • 在无缓冲通道上发送和接收是同步的。 该文档可能会有所帮助:golang.org/doc/effective_go.html#channels

msgs通道未缓冲。 这意味着要完成发送,必须有一个相应的接收操作也必须完成。 这在goroutines之间提供了一个同步点。

很容易看出您是否仅在示例中添加了一些打印语句

http://play.golang.org/p/diYQGN-iwE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func produce() {
    for i := 0; i < 4; i++ {
        fmt.Println("sending")
        msgs <- i
        fmt.Println("sent")
    }
    fmt.Println("Before closing channel")
    close(msgs)
    fmt.Println("Before passing true to done")
    done <- true
}

func consume() {
    for msg := range msgs {
        fmt.Println("Consumer:", msg)
        time.Sleep(100 * time.Millisecond)

    }
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sending
Consumer:  0
sent
sending
Consumer:  1
sent
sending
Consumer:  2
sent
sending
Consumer:  3
sent
Before closing channel
Before passing true to done
After calling DONE
  • 您也可以通过编辑原件以使其类似于var msgs = make(chan int, 99)来查看此内容。具有足够空间的缓冲通道。在这种情况下,主电源可能会在消费者可以消费任何东西之前退出(确切的顺序是不确定的)。
  • 完美现在很有意义。感谢您的详细解释,@ JimB。
  • 我厌倦了使msgs通道成为缓冲。在consume()中添加了一些打印语句。我看到每次运行代码时,都会打印" fmt.Println("调用DONE之后")",即使在那之后,我仍然可以看到来自consumpt()函数内部的打印语句被打印。在这种情况下,每当" <-done"收到消息时,它不等待任何goroutine完成,这是不正确的吗?那么,为什么在这种情况下,即使收到<-done并且打印了"调用DONE之后",我仍然可以看到consumpt()goroutine继续运行?
  • @srock:使用者可以自由运行直到主返回为止,因此它可以打印出<-done与运行时关闭之间的某些行。