首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >戈朗-为什么要使用done频道?

戈朗-为什么要使用done频道?
EN

Stack Overflow用户
提问于 2016-10-06 03:01:26
回答 4查看 10.4K关注 0票数 6

这是Golang的一个示例代码。但无法理解为什么在这种情况下需要“完成”通道。

https://gobyexample.com/closing-channels

没有理由将真消息发送到to频道。我们可以知道,当“发送所有作业”消息被打印出来时,作业通道就被完成了,不是吗?

我删除了相对于to通道和结果仍然相同的代码。

https://play.golang.org/p/xOmzobFpTQ

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-10-06 03:56:10

不,结果不一样:

在许多情况下(例如,不同的CPU负载,并且它是不确定的和系统依赖的行为),您的主要goroutine在您的received job goroutine之前退出,因此您不能保证all jobs received,例如只添加

代码语言:javascript
运行
复制
time.Sleep(500)

在此之前

代码语言:javascript
运行
复制
fmt.Println("received job", j)

要查看此,请在围棋游乐场上试用

代码语言:javascript
运行
复制
// _Closing_ a channel indicates that no more values
// will be sent on it. This can be useful to communicate
// completion to the channel's receivers.

package main

import (
    "fmt"
    "time"
)

// In this example we'll use a `jobs` channel to
// communicate work to be done from the `main()` goroutine
// to a worker goroutine. When we have no more jobs for
// the worker we'll `close` the `jobs` channel.
func main() {
    jobs := make(chan int, 5)
    //done := make(chan bool)

    // Here's the worker goroutine. It repeatedly receives
    // from `jobs` with `j, more := <-jobs`. In this
    // special 2-value form of receive, the `more` value
    // will be `false` if `jobs` has been `close`d and all
    // values in the channel have already been received.
    // We use this to notify on `done` when we've worked
    // all our jobs.
    go func() {
        for {
            j, more := <-jobs
            if more {
                time.Sleep(500)
                fmt.Println("received job", j)
            } else {
                fmt.Println("received all jobs")
                //done <- true
                return
            }
        }
    }()

    // This sends 3 jobs to the worker over the `jobs`
    // channel, then closes it.
    for j := 1; j <= 3; j++ {
        jobs <- j
        fmt.Println("sent job", j)
    }
    close(jobs)
    fmt.Println("sent all jobs")

    // We await the worker using the
    // [synchronization](channel-synchronization) approach
    // we saw earlier.
    //<-done
}

产出:

代码语言:javascript
运行
复制
sent job 1
sent job 2
sent job 3
sent all jobs

而不是:

代码语言:javascript
运行
复制
sent job 1
received job 1
received job 2
sent job 2
sent job 3
received job 3
received all jobs
sent all jobs

请参见:

如果包括time.Sleep,Goroutine就不会执行

为什么time.sleep需要运行特定的峡谷呢?

围棋中奇怪的频道行为

票数 6
EN

Stack Overflow用户

发布于 2016-10-06 03:22:31

有一种比赛条件--你很幸运。

如果您没有done通道,那么程序的输出是不确定的。

根据线程执行顺序,主线程可能在goroutine完成其处理之前退出,从而导致goroutine在中途被杀死。

通过强制主线程从done通道读取数据,我们强迫主线程等待,直到在done通道中消耗了一些数据。这为我们提供了一种整洁的同步机制,在这种机制中,goroutine通知主线程它是通过写入done通道来完成的。这反过来会导致主线程的阻塞<- done完成,并导致程序终止。

票数 5
EN

Stack Overflow用户

发布于 2018-01-06 22:14:53

我认为被接受的答案并没有说明确切的原因。

go语言属于过程范式,意味着每一条指令都是线性执行的。当一个go例程从主go例程中分叉时,它就开始了它自己的小冒险,留下主线程返回。

缓冲通道的容量为5,这意味着在缓冲区满之前它不会阻塞。如果它是空的,它也会阻塞(容量为零的信道本身是无缓冲的)。

因为只有4次迭代(0到<=3),所以读取操作不会阻塞。

通过指示主线程从完成的通道读取,我们强迫主线程等待,直到完成通道中有一些数据被消耗。当迭代结束时,执行of分支,而写入操作done <- true将导致主线程中的<- done读取操作的释放。读取操作等待从done中提取现在插入的值。

done读取之后,主Go例程不再被阻塞,因此成功终止。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39886840

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档