英文:
go routine not printing `shutdown msg` after closing channel `done`
问题
两个Go协程从同一个通道读取。第一个Go协程在done
通道关闭后从未打印其关闭消息,而第二个Go协程总是打印。
为什么第一个Go协程的消息没有打印出来,而且这个方法是否在return
?
main.go
func main() {
done := make(chan bool)
c := make(chan os.Signal, 1)
cameras := client.CameraConfig()
client.DrawUserControls(cameras)
operator := client.NewOperator(cameras)
go operator.UserInputListener(done)
go operator.ParseAndExecuteUserCommand(done)
signal.Notify(c, os.Interrupt)
for range c {
close(done)
break
}
log.Println("Interrupt signal received. Shutting client down....")
time.Sleep(5 * time.Second)
}
client.go
func (o *Operator) UserInputListener(done <-chan bool) {
reader := bufio.NewReader(os.Stdin)
for {
select {
case <-done:
log.Println("Keyboard listener shutting down.") // <-- this never prints
return
default:
line, _, err := reader.ReadLine()
if err != nil {
log.Println(err)
}
data := strings.Split(string(line), "")
id, err := strconv.Atoi(data[1])
if err != nil {
log.Println(err)
continue
}
switch data[0] {
case "b":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "run",
}
case "t":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "terminate",
}
case "r":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "record",
}
case "s":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "stop",
}
}
}
}
}
func (o *Operator) ParseAndExecuteUserCommand(done <-chan bool) {
for {
select {
case <-done:
log.Println("Command operator shutting down.")
return
case ctrl := <-o.Controls:
switch ctrl.Ctrl {
case "run":
o.Room[ctrl.Identifier].Run()
case "terminate":
o.Room[ctrl.Identifier].Close()
case "record":
o.Room[ctrl.Identifier].Write()
case "stop":
o.Room[ctrl.Identifier].Stop()
}
}
}
}
英文:
Two go routines reading from the same channel. The first go routine never prints its shutdown message after the done
channel is closed, while the second go routine always does.
Why is the message from the first go routine not printing and is the method even returning
?
main.go
func main() {
done := make(chan bool)
c := make(chan os.Signal, 1)
cameras := client.CameraConfig()
client.DrawUserControls(cameras)
operator := client.NewOperator(cameras)
go operator.UserInputListener(done)
go operator.ParseAndExecuteUserCommand(done)
signal.Notify(c, os.Interrupt)
for range c {
close(done)
break
}
log.Println("Interrupt signal received. Shutting client down....")
time.Sleep(5 * time.Second)
}
client.go
func (o *Operator) UserInputListener(done <-chan bool) {
reader := bufio.NewReader(os.Stdin)
for {
select {
case <-done:
log.Println("Keyboard listener shutting down.") // <-- this never prints
return
default:
line, _, err := reader.ReadLine()
if err != nil {
log.Println(err)
}
data := strings.Split(string(line), "")
id, err := strconv.Atoi(data[1])
if err != nil {
log.Println(err)
continue
}
switch data[0] {
case "b":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "run",
}
case "t":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "terminate",
}
case "r":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "record",
}
case "s":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "stop",
}
}
}
}
}
func (o *Operator) ParseAndExecuteUserCommand(done <-chan bool) {
for {
select {
case <-done:
log.Println("Command operator shutting down.")
return
case ctrl := <-o.Controls:
switch ctrl.Ctrl {
case "run":
o.Room[ctrl.Identifier].Run()
case "terminate":
o.Room[ctrl.Identifier].Close()
case "record":
o.Room[ctrl.Identifier].Write()
case "stop":
o.Room[ctrl.Identifier].Stop()
}
}
}
}
答案1
得分: 2
原因是因为你创建了同步通道,并在这里推送了1条消息,然后你只能读取一次。这是因为你只从done
通道中获取1次(随机)读取。
你可以使用WaitGroup
来关闭goroutine:
main.go:
var (
done chan bool
)
func main() {
cameras := client.CameraConfig()
client.DrawUserControls(cameras)
operator := client.NewOperator(cameras)
done = make(chan bool, 1)
wg := &sync.WaitGroup{}
wg.Add(2)
go operator.UserInputListener(done, wg)
go operator.ParseAndExecuteUserCommand(done, wg)
handleShutdown()
wg.Wait()
}
func handleShutdown() {
ch := make(chan os.Signal, 1)
go func() {
<-ch //等待应用程序终止
log.Println("Shutdown received.")
close(done)
}()
signal.Notify(ch, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
}
client.go:
func (o *Operator) UserInputListener(done <-chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-done:
log.Println("Keyboard listener shutting down.")
return
........
}
}
}
func (o *Operator) ParseAndExecuteUserCommand(done <-chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-done:
log.Println("Command operator shutting down.")
return
........
}
}
}
使用此链接获取详细信息
英文:
The reason is because you have created synchronous channel and you push here 1 message and then you could read it only once as well. That is because you get only 1 (random) read from done
channel.
The way you can shut down your goroutines is to use WaitGroup
:
main.go:
var (
done chan bool
)
func main() {
cameras := client.CameraConfig()
client.DrawUserControls(cameras)
operator := client.NewOperator(cameras)
done = make(chan bool, 1)
wg := &sync.WaitGroup{}
wg.Add(2)
go operator.UserInputListener(done, wg)
go operator.ParseAndExecuteUserCommand(done, wg)
handleShutdown()
wg.Wait()
}
func handleShutdown() {
ch := make(chan os.Signal, 1)
go func() {
<-ch //wait for application terminating
log.Println("Shutdown received.")
close(done)
}()
signal.Notify(ch, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
}
client.go:
func (o *Operator) UserInputListener(done <-chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-done:
log.Println("Keyboard listener shutting down.")
return
........
}
}
}
func (o *Operator) ParseAndExecuteUserCommand(done <-chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-done:
log.Println("Command operator shutting down.")
return
........
}
}
}
Use this link for details
1: http://www.hydrogen18.com/blog/stopping-it-all-in-go.html "link"
答案2
得分: 0
UserInputListener
无法进入case <-done:
的原因是它在以下代码行中被阻塞等待输入:
line, _, err := reader.ReadLine()
这一行是阻塞的!
这种问题的解决方案是什么?
这并不容易。
读取例程
你可以使用另一个go例程来进行读取,将数据发送到一个通道中,在select语句中从该通道读取数据,同时也从done
通道中读取数据。这样可以正确关闭UserInputListener
,但是可能无法正确关闭其他的goroutine。但也许这并不重要...?
func (o *Operator) UserInputListener(done <-chan bool) {
// 带有一定缓冲区的通道,这样读取器就不必等待(太多)
ch := make(chan string, 10)
go func() {
reader := bufio.NewReader(os.Stdin)
for {
line, _, err := reader.ReadLine()
if err != nil {
log.Println(err)
// 在错误时停止?
// return
}
ch <- string(line)
}
}()
for {
select {
case <-done:
log.Println("键盘监听器关闭。") // <-- 这句话永远不会打印
return
case line := <-ch:
data := strings.Split(line, " ")
id, err := strconv.Atoi(data[1])
if err != nil {
log.Println(err)
continue
}
switch data[0] {
case "b":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "run",
}
case "t":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "terminate",
}
case "r":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "record",
}
case "s":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "stop",
}
}
}
}
}
关闭读取器源
你也可以尝试关闭reader
正在读取的内容。我已经在其他上下文中使用过这个解决方案,比如从串行设备读取。
然而,这在使用os.Stdin
时不起作用,正如JimB指出的那样。os.StdIn.Close()
会阻塞,因为它等待读取器完成。
英文:
The reason UserInputListener
is not entering case <-done:
is because it is stuck waiting for some input at this line:
line, _, err := reader.ReadLine()
This line is blocking!
What is the solution for this type of problem?
This is not easy.
Read Routine
You could use another go routine to do the reading, send the data into a channel that you read from in the select where you also read from the done
channel. That would properly close the UserInputListener
but leave the other goroutine to not be closed properly. But maybe that does not matter as much...?
func (o *Operator) UserInputListener(done <-chan bool) {
// channel with some buffer so reader doesn't have to wait (so much)
ch := make(chan string, 10)
go func() {
reader := bufio.NewReader(os.Stdin)
for {
line, _, err := reader.ReadLine()
if err != nil {
log.Println(err)
// stop on error?
// return
}
ch <- string(line)
}
}()
for {
select {
case <-done:
log.Println("Keyboard listener shutting down.") // <-- this never prints
return
case line:= <-ch:
data := strings.Split(line, "")
id, err := strconv.Atoi(data[1])
if err != nil {
log.Println(err)
continue
}
switch data[0] {
case "b":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "run",
}
case "t":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "terminate",
}
case "r":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "record",
}
case "s":
o.Controls <- Ctrl{
Identifier: id,
Ctrl: "stop",
}
}
}
}
}
Close Reader Source
You could also try to close whatever the reader
is reading from. I have already used this solution in other contexts like reading from serial devices.
This does not work with os.Stdin
however as JimB pointed out to me. os.StdIn.Close()
will block as it waits for the reader to finish.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论