Concurrency 如何关闭等待I/O的goroutine

Concurrency 如何关闭等待I/O的goroutine,concurrency,go,goroutine,Concurrency,Go,Goroutine,我已经创建了两个go例程发送方和接收方,发送方将连续从用户(键盘)获取数据并写入流,接收方将独立地从流中获取值并将其打印到屏幕上。两者都是使用go例程并发的 在某个时间点,接收器出现故障,关闭连接并退出接收器go例程,但等待用户输入(i/o操作)的发送器go例程将不会关闭。如何退出此场景中的所有go例程 下面是这个场景的一段示例代码 package main import ( "fmt" "time" ) var stop bool = false func sender(

我已经创建了两个go例程发送方接收方,发送方将连续从用户(键盘)获取数据并写入流,接收方将独立地从流中获取值并将其打印到屏幕上。两者都是使用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的例行程序。


请在这个问题上帮助我。

首先,您的代码围绕
stop
变量展开了一场竞赛。当存在数据竞争时,无法保证您的程序将按照定义的方式运行。使用通道同步goroutine。然而,这并不是您继续执行该程序的原因

您的代码在
fmt.Scanf
上阻塞,无法检查
停止
条件。由于Stdin上的读取无法中断(发生在
fmt.Scanf
内部),因此在再次调用
Scanf
之前,需要检查停止条件。如果没有更多的输入,但是您在Stdin上有一个挂起的读取,最简单的处理方法就是让goroutine保持运行。有一些相当复杂的方法可以使用所谓的“自我管道”技巧来打破这种局面,但通常不值得付出努力,因为goroutine很小,并且不占用很多资源

for !stop {
    fmt.Scanf("%s", &str)
    fmt.Println("Entered :", str)
    // use a channel to detect when to exit
    select {
    case <-stop:
        return
    default:
    }
}
for!停止{
格式扫描(“%s”和“str”)
fmt.Println(“输入:”,str)
//使用通道检测何时退出
挑选{

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

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

package main

import "fmt"

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(&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
}
主程序包
输入“fmt”
var pipe=make(chan string)//共享用户输入的文本
var stop=make(chan bool)//共享停止信号
func listen(){
为了{
变量输入字符串
fmt.扫描(和输入)

管道侧注:同时读取和写入同一个变量(
stop
)是不安全的。千万不要这样做。这是一个典型的数据竞争,有。Go's可以向您指出这些。在这个示例中,您实际上没有发送任何内容。但是,如果您只是想要原始数据(字符串、字节)然后,
io.Pipe
可能很方便,因为双方都执行
延迟myside.Close()
(其中
myside
是管道的读或写端,视情况而定)。在一般情况下,您会使用一个通道发送数据,写入程序可以关闭该通道以表示数据已完成;但读卡器不应该/不能关闭;这通常意味着您需要一个单独的通道或其他同步机制来发出写入程序退出的信号。感谢您的响应。在您的解决方案中,在执行fmt.Scanf之后,只有执行当fmt没有输入时,将选择并返回我的问题。然后扫描如何关闭go例程。@sujin:正如回答中所解释的,您不能直接中断对stdin的读取(这不是go问题,在POSIX中,您不能中断从另一个线程读取的
Read
)。您可以使用
select
syscall仅在数据可用时读取数据,并从goroutine异步返回,但何必麻烦呢?如果您知道输入的结尾应该是什么,那么返回。如果输入流结束,您将获得
EOF
。如果您不知道输入流的结尾是什么,则您可能有错误或者,或者您仍然需要保持读取打开状态。在您的示例中,代码listen()go例程在5 write at by write()go例程之后不会退出,因为它将留在for循环中,这可能是一个泄漏。只有main()和write()将正确退出。在主脚本末尾使用panic语句并检查堆栈跟踪。此场景的预期解决方案我同意JimB;如果无法避免扫描,最容易让goroutine保持运行。但这或可能有帮助吗?