为什么程序在select中被通道阻塞?

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

Why the program blocks by the channel in the select?

问题

package main

import (
	"fmt"
)

type A struct{
	exit chan bool
}

func (a *A) f(){
	select{
        //the routine process

        //quit
		case <- a.exit:

				fmt.Println("-----over-----")
				a.exit <- true
				fmt.Println("+++++over++++++")				
		}
}

func main() {

	a := A{}
	
	go a.f()
	a.exit = make(chan bool)
	
	a.exit <- true
}

我想运行多个 goroutine,并且我希望让主函数通知其他 goroutine 退出。这是我的代码,但程序在 select 中阻塞,程序只输出 "-----over-----",没有输出 "+++++over++++++",代码有什么问题?感谢您的帮助。

英文:
package main

import (
	&quot;fmt&quot;
)

type A struct{
	exit chan bool
}

func (a *A) f(){
	select{
        //the routine process

        //quit
		case &lt;- a.exit:

				fmt.Println(&quot;-----over-----&quot;)
				a.exit &lt;- true
				fmt.Println(&quot;+++++over++++++&quot;)				
		}
}

func main() {

	a := A{}
	
	go a.f()
	a.exit = make(chan bool)
	
	a.exit &lt;- true
}

I'd like to run muti goroutines,and I want let the main func to notice other goroutine to quit.
here is my code,but the program block in the select,the program only output "-----over-----",without "+++++over++++++",what's wrong with the code?Grateful for your help.

答案1

得分: 0

你的程序阻塞是因为你写的代码导致的,考虑以下操作顺序:

  1. main 协程启动 a.f 协程。
  2. a.f 协程阻塞,尝试从空通道 a.exit 中读取数据。
  3. maina.exit 设置为无缓冲通道,a.f 协程现在阻塞在读取新通道上,这是允许的。
  4. maina.exit 写入一个值,a.fa.exit 中读取该值,这同步了两个协程,现在都不再阻塞。
  5. a.f 协程现在阻塞,尝试向无缓冲的 a.exit 通道写入数据,这将永远不会解除阻塞,因为没有其他协程会尝试从该通道中读取数据。
  6. main 现在退出,并导致所有其他协程退出,这可能会在第5步之前发生。

所以你的程序从不输出 +++++over++++++ 的原因是:

  • 你的 a.f 协程在 a.exit <- true 处阻塞,因为没有其他协程会从该通道中读取该值。
  • 你的 main 协程很可能会在 a.f 完成工作之前退出并终止整个程序。

我认为你在询问如何在协程完成后使 main 退出,以下是最简单的示例:

package main

import (
	"fmt"
)

type A struct {
	exit chan struct{}
}

func (a *A) f() {
	defer close(a.exit) // 在 f() 完成后关闭通道,关闭的通道会产生零值,所以 <-a.exit 会解除阻塞

	fmt.Println("+++++over++++++")
}

func main() {
	a := A{}
	go a.f()
	a.exit = make(chan struct{})

	<-a.exit
}
英文:

Your program blocks because that is what you have written, consider this order of operations:

  1. main goroutine starts a.f goroutine.
  2. a.f blocks trying to read from the nil channel a.exit.
  3. main sets a.exit to be an unbuffered channel, a.f is now blocked reading from the new channel, this is allowed.
  4. main writes a value into a.exit and a.f reads the value from a.exit, this synchronises the goroutines, nether are blocked now.
  5. a.f now blocks trying to write into the unbuffered a.exit, this will never unblock because nobody will ever try to read from the channel again.
  6. main now exits and causes all other goroutines to exit, this might happen before step 5.

So the reasons your program never outputs +++++over++++++ are:

  • Your a.f goroutine blocks at a.exit &lt;- true because no other goroutine will read this value from the channel.
  • Your main goroutine will probably exit and terminate the entire program before a.f can finish working.

I think you are asking how to make main exit after the goroutine is finished, this is the most simple example of that:

package main

import (
	&quot;fmt&quot;
)

type A struct {
	exit chan struct{}
}

func (a *A) f() {
	defer close(a.exit) // Close the chanel after f() finishes, closed channels yield the zero-value so &lt;-a.exit will unblock

	fmt.Println(&quot;+++++over++++++&quot;)
}

func main() {
	a := A{}
	go a.f()
	a.exit = make(chan struct{})

	&lt;-a.exit
}

huangapple
  • 本文由 发表于 2021年11月24日 16:18:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/70092688.html
匿名

发表评论

匿名网友

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

确定