英文:
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)
}
我的疑问:
-
我的理解是,一旦调用
return
语句,函数将终止,并且该函数内部的通道不再存在。 -
return
语句只被调用一次。但是out
通道的内容被多次读取。在这种情况下,实际的执行流程是什么?
(我对并发编程还不熟悉。)
英文:
I'm trying with Go channels and confused with below function example from go blog:
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
}
Main :
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)
}
Complete Code: http://play.golang.org/p/Qh30wzo4m0
My Doubts:
-
My understanding was, once
return
is called the function will be terminated and the channel inside that function has no more life. -
The
return
statement is called only once. But the content of theout
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
仍然会运行。
当
gen()
返回时,仍然存在对out
通道的引用,这使得它不会被垃圾回收。
无论gen()
是否有一个使用该通道的go例程闭包都无关紧要。当一个通道不再使用时,发送者可以显式关闭通道。
然后,通道的选择语句将使go例程退出。
最后,一切都会被清理。
英文:
out := make(chan int)
This is not a buffered channel, which means the out <- n
will block until someone somewhere reads that channel (the fmt.Println(<-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 <- 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(<-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(<-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 (
"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()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论