如何关闭等待I/O的goroutine

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

How to Close the goroutine which waiting on I/O

问题

我创建了两个Go协程,一个是sender(发送者),一个是receiver(接收者)。发送者将持续从用户(键盘)获取数据并写入流,接收者将独立地从流中获取值并将其打印到屏幕上。两者都是并发执行的Go协程。

在某个时间点,接收者发生故障并关闭连接,同时退出接收者Go协程,但是等待用户输入(I/O操作)的发送者Go协程将不会关闭。在这种情况下,如何退出所有的Go协程?

以下是针对此场景的示例代码:

package main

import (
    "fmt"
    "time"
)

var stop bool = false

func sender() {
    str := ""
    for !stop {
        fmt.Scanf("%s", &str)
        fmt.Println("Entered:", str)
    }
    fmt.Println("Closing sender goroutine")
}

func receiver() {
    i := 0
    for !stop {
        i++
        if i > 5 {
            stop = true
        }
        time.Sleep(1 * time.Second)
    }
    fmt.Println("Closing receiver goroutine")
}

func main() {
    go sender()
    go receiver()

    /* Wait for goroutines to finish */
    for !stop {
        time.Sleep(1 * time.Millisecond)
    }
    time.Sleep(1 * time.Second)

    panic("Display stack")
}

上述代码中,发送者将在等待用户输入后继续执行,而在循环5次后,接收者将退出接收者Go协程。我期望当接收者关闭时,等待I/O的Go协程也会关闭。

请帮助我解答这个问题。

英文:

I have created two go routines sender and receiver, sender will continuous get the data from the user(keyboard) and write to stream, receiver will independently get the value from stream print it to the screen. Both are concurrent using go routine

At some point of time receiver failed and close the connection as well as exit the receiver go routine, but sender go routine which waiting for user input(i/o operation) will not be closed. How to exit all the go routines in this scenario?

Below is the piece of sample code for this scenario.

package main

import (
    "fmt"
    "time"
)

var stop bool = false

func sender() {
    str := ""
    for !stop {
        fmt.Scanf("%s", &str)
        fmt.Println("Entered :", str)
    }   
    fmt.Println("Closing sender goroutine")
}

func receiver() {
    i := 0
    for !stop {
        i++
        if i > 5 { 
            stop = true
        }
        time.Sleep(1 * time.Second)
    }   
    fmt.Println("Closing receiver goroutine")
}

func main() {
    go sender()
    go receiver()

    /* Wait for goroutines to finish */
    for !stop {
        time.Sleep(1 * time.Millisecond)
    }   
    time.Sleep(1 * time.Second)

    panic("Display stack")
}

Above code sender will wait for user input after 5 loop receiver will exit the receiver go routine. I expect when receiver close, go routine which waiting on i/o has to be closed.

Kindly help me on this question.

答案1

得分: 3

如Dave C和JimB所说,使用通道来协调goroutine。以下是一个可能会有所帮助的示例。

在接收到用户的5条消息后退出:

package main

import "fmt"

var pipe = make(chan string) // 用于共享用户输入的文本
var stop = make(chan bool)   // 用于共享停止信号

func listen() {
    for {
        var input string
        fmt.Scan(&input)
        pipe <- input
    }
}

func write() {
    for i := 0; i < 5; i++ {
        var output string
        output = <-pipe
        fmt.Println("Received", output)
    }

    stop <- true
}

func main() {
    go listen()
    go write()

    <-stop
}

希望对你有所帮助!

英文:

As Dave C & JimB say, use channels to coordinate goroutines. Here's an example that may help.

Exit after receiving 5 messages from the user:

package main

import &quot;fmt&quot;

var pipe = make(chan string) //shares text entered by user
var stop = make(chan bool)   //shares stop signal

func listen() {
	for {
		var input string
		fmt.Scan(&amp;input)
		pipe &lt;- input
	}
}

func write() {
	for i := 0; i &lt; 5; i++ {
		var output string
		output = &lt;-pipe
		fmt.Println(&quot;Received&quot;, output)
	}

	stop &lt;- true
}

func main() {
	go listen()
	go write()

	&lt;-stop
}

答案2

得分: 1

首先,你的代码存在一个关于stop变量的竞争条件。当存在数据竞争时,无法保证程序的行为与定义一致。使用通道来同步goroutine。然而,这并不是导致你的程序继续运行的原因。

你的代码在fmt.Scanf上阻塞,并且无法检查stop条件。由于在fmt.Scanf内部发生的Read无法被中断,所以你需要在调用Scanf之前检查停止条件。如果没有更多的输入,但是在Stdin上有一个挂起的Read,最简单的处理方法是让该goroutine继续运行。还有一些相当复杂的方法可以使用"self-pipe"技巧来打破这种情况,但通常不值得这样做,因为goroutine很小且不占用太多资源。

for {
    fmt.Scanf("%s", &str)
    fmt.Println("Entered :", str)
    // 使用通道或上下文来检测何时退出
    select {
    case <-ctx.Done():
        return
    default:
    }
}
英文:

To start, your code has a race around the stop variable. When there's a data race, there's no guarantee your program will behave as defined. Use channels to synchronize goroutines. This however isn't why you program continues.

Your code is blocking on fmt.Scanf, and doesn't get to check the stop condition. Since a Read on Stdin can't be interrupted (which is happening inside fmt.Scanf), you need to check for the stop condition before calling Scanfagain. If there's no more input, but you have a pending Read on Stdin, the easiest way to handle it is to just let leave that goroutine running. There are some rather complex ways to break out of this using something known as the "self-pipe" trick, but it's generally not worth the effort, as goroutines are small and don't take many resources.

for {
    fmt.Scanf(&quot;%s&quot;, &amp;str)
    fmt.Println(&quot;Entered :&quot;, str)
    // use a channel or context to detect when to exit
    select {
    case &lt;-ctx.Done():
        return
    default:
    }
}

huangapple
  • 本文由 发表于 2015年5月31日 00:16:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/30548428.html
匿名

发表评论

匿名网友

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

确定