返回通道 Golang

huangapple go评论94阅读模式
英文:

Returning Channels Golang

问题

我正在使用Go通道,并对Go博客中的以下函数示例感到困惑:

func gen(nums []int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    fmt.Println("return statement is called ")
    return out
}

func main() {
    c := make(chan int)
    c = gen([]int{2, 3, 4, 5})

    // Consume the output.
    // Print 2,3,4,5
    fmt.Println(<-c)
    fmt.Println(<-c)
    fmt.Println(<-c)
    fmt.Println(<-c)
}

我的疑问:

  1. 我的理解是,一旦调用return语句,函数将终止,并且该函数内部的通道不再存在。

  2. return语句只被调用一次。但是out通道的内容被多次读取。在这种情况下,实际的执行流程是什么?

(我对并发编程还不熟悉。)

英文:

I'm trying with Go channels and confused with below function example from go blog:

func gen(nums []int) &lt;-chan int {
    out := make(chan int)
    go func() {
	    for _, n := range nums {
		    out &lt;- n
	    }
	    close(out)
    }()
    fmt.Println(&quot;return statement is called &quot;)
    return out
}

Main :

func main() {
    c := make(chan int)
    c = gen([]int{2, 3, 4, 5})

    // Consume the output.
    // Print 2,3,4,5
    fmt.Println(&lt;-c)
    fmt.Println(&lt;-c)
    fmt.Println(&lt;-c)
    fmt.Println(&lt;-c)
}

Complete Code: http://play.golang.org/p/Qh30wzo4m0

My Doubts:

  1. My understanding was, once return is called the function will be terminated and the channel inside that function has no more life.

  2. The return statement is called only once. But the content of the out channel is read many times. In this case what is the actual flow of execution?

(I'm new to concurrent programming.)

答案1

得分: 19

out := make(chan int)

这是一个非缓冲通道,这意味着out <- n会被阻塞,直到有其他地方读取该通道(即fmt.Println(<-c)的调用)。
(参见“do golang channels maintain order”)

因此,在gen()函数末尾的返回并不意味着字面上的go func()已经终止(因为它仍在等待读取者消费out通道的内容)。

但是main函数从gen()函数中获取out通道作为返回值。
gen()终止后如何获取它?

gen()终止并不影响其返回值(即out通道):gen()的目标是“生成”该out通道。

main函数可以在gen()终止后长时间使用out(作为gen()的返回值)。

即使gen()终止,gen()内部的go func仍然会运行。


正如vagabond评论中所指出的:

gen()返回时,仍然存在对out通道的引用,这使得它不会被垃圾回收。
无论gen()是否有一个使用该通道的go例程闭包都无关紧要。

当一个通道不再使用时,发送者可以显式关闭通道。
然后,通道的选择语句将使go例程退出。
最后,一切都会被清理。

英文:
out := make(chan int)

This is not a buffered channel, which means the out &lt;- n will block until someone somewhere reads that channel (the fmt.Println(&lt;-c) calls)
(See also "do golang channels maintain order")

So the return at the end of the gen() function doesn't mean the literal go func() is terminated (since it is still waiting for readers to consume the content of the out channel).

> But main function getting out channel as return from gen() function.
How it is possible to get it after gen() is terminated?

The fact that gen() terminates has no effect on its returned value (the out channel): the goal of "gen()" is to "generate" that out channel.

main can use out (as the returned value of gen()) long after gen() terminates.

The literal go func within gen() still runs, even if gen() is terminated.


As noted by vagabond in the comments:

> When gen() returns, there is still a reference to the out channel, which makes it not garbage collected.
It doesn't matter if the gen() has a go routine closure is using the channel.
>
> When a channel is not used, the sender can close the channel explicitly.
And then the select for the channel will make the go routine to exit.
At last, everything will be cleared.

答案2

得分: 4

因为gen()函数作为一个goroutine触发了通道填充函数;

go func() {
    for _, n := range nums {
        out <- n
    }
    close(out)
}()

当第一个值被发送到out通道时,它会阻塞,因为还没有接收者(无缓冲通道在发送时会阻塞,直到有接收者接收),所以这个goroutine在gen()函数返回时不会结束。

main()函数中,从c通道接收值:

fmt.Println(<-c)
...

这会导致在读取结果时,gen()函数启动的goroutine继续填充通道,最后当goroutine返回时,main()函数也会返回,因为在out上没有剩余的值可发送,也没有剩余的值可在c上接收。

另外,在main()函数中的c := make(<-chan int)是不必要的,因为gen()函数创建并返回了一个通道。

参考Playground

英文:

Because gen() fires off the channel population function as a goroutine;

go func() {
    for _, n := range nums {
        out &lt;- n
    }
    close(out)
}()

and it blocks when the first value is sent on the out channel, because nothing is receiving yet (unbuffered channels block on sending until something receives on them), that goroutine doesn't end when the gen() function returns.

The receives from c in main()

fmt.Println(&lt;-c)
...

then cause the goroutine started in gen() to keep populating the channel as the results are read out, and then finally main() returns when the goroutine returns, because there is nothing left to send on out, and nothing left to receive on c.

Also, the c := make(&lt;-chan int) in main() is unnecessary as gen() creates a channel and returns it.

See Playground

答案3

得分: -2

我认为这是你想要的:https://play.golang.org/p/WSBUPPyQiY6

package main

import (
	"fmt"
	"sync"
	"time"
)

func read() (elemChan chan int) {
	elemChan = make(chan int)
	go func() {
		for k := 0; k < 1000; k++ {
			elemChan <- k
		}
		close(elemChan)
	}()
	return
}

func main() {
	fmt.Println("Hello, playground")
	elemChan := read()
	wg := sync.WaitGroup{}
	for k := 0; k < 2; k++ {
		wg.Add(1)
		go func(k int) {
			for {
				e, more := <-elemChan
				if !more {
					wg.Done()
					return
				}
				fmt.Printf("goroutine #%d: %d\n", k, e)
				time.Sleep(1000 * time.Nanosecond)
			}
		}(k)
	}
	wg.Wait()
}
英文:

I think this is what you want: https://play.golang.org/p/WSBUPPyQiY6

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

func read() (elemChan chan int) {
	elemChan = make(chan int)
	go func() {
		for k := 0; k &lt; 1000; k++ {
			elemChan &lt;- k
		}
		close(elemChan)
	}()
	return
}

func main() {
	fmt.Println(&quot;Hello, playground&quot;)
	elemChan := read()
	wg := sync.WaitGroup{}
	for k := 0; k &lt; 2; k++ {
		wg.Add(1)
		go func(k int) {
			for {
				e, more := &lt;-elemChan
				if !more {
					wg.Done()
					return
				}
				fmt.Printf(&quot;goroutine #%d: %d\n&quot;, k, e)
				time.Sleep(1000 * time.Nanosecond)
			}
		}(k)
	}
	wg.Wait()
}

huangapple
  • 本文由 发表于 2015年2月19日 17:08:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/28602308.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定