如何有效地关闭两个goroutine?

如何有效地关闭两个goroutine?,go,Go,我正在使用两个并发goroutine将stdin/stdout从终端复制到net.Conn目标。出于某种原因,我无法完全停止这两个go例程而不出现紧急错误(因为试图关闭一个关闭的连接)。这是我的代码: func interact(c net.Conn, sessionMap map[int]net.Conn) { quit := make(chan bool) //the channel to quit copy := func(r io.ReadCloser, w io.Wr

我正在使用两个并发goroutine将stdin/stdout从终端复制到net.Conn目标。出于某种原因,我无法完全停止这两个go例程而不出现紧急错误(因为试图关闭一个关闭的连接)。这是我的代码:

func interact(c net.Conn, sessionMap map[int]net.Conn) {
    quit := make(chan bool) //the channel to quit

    copy := func(r io.ReadCloser, w io.WriteCloser) {
        defer func() {
            r.Close()
            w.Close()
            close(quit) //this is how i'm trying to close it
        }()

        _, err := io.Copy(w, r)

        if err != nil {
            //
        }

    }

    go func() {
        for {
            select {
            case <-quit:
                return
            default:
                copy(c, os.Stdout)
            }
        }
    }()

    go func() {
        for {
            select {
            case <-quit:
                return
            default:
                copy(os.Stdin, c)
            }
        }
    }()
}
func交互(c net.Conn,sessionMap[int]net.Conn){
quit:=make(chan bool)//要退出的频道
复制:=func(r io.ReadCloser,w io.WriteCloser){
延迟函数(){
r、 关闭()
w、 关闭()
关闭(退出)//这就是我试图关闭它的方式
}()
_,err:=io.Copy(w,r)
如果错误!=零{
//
}
}
go func(){
为了{
挑选{

case您不能在一个频道上多次调用
close
,没有理由在for循环中调用
copy
,因为它只能运行一次,而且您在错误的方向上复制,写入stdin并从stdout读取

简单地询问如何退出2个goroutine很简单,但这不是您在这里需要做的唯一事情。因为
io.Copy
是阻塞的,所以您不需要额外的同步来确定调用何时完成。这让您大大简化了代码,这将使您更容易推理

func interact(c net.Conn) {
    go func() {
        // You want to close this outside the goroutine if you
        // expect to send data back over a half-closed connection
        defer c.Close()

        // Optionally close stdout here if you need to signal the
        // end of the stream in a pipeline.
        defer os.Stdout.Close()

        _, err := io.Copy(os.Stdout, c)
        if err != nil {
            //
        }
    }()

    _, err := io.Copy(c, os.Stdin)
    if err != nil {
        //
    }
}

还要注意的是,您可能无法从stdin中断
io.Copy
,因此您不能期望
interact
函数返回。在函数体中手动执行
io.Copy
,并检查每个循环上的半封闭连接可能是一个好主意,这样您就可以更快地中断,并确保完全恢复关闭
net.Conn

也可能是这样

 func scanReader(quit chan int, r io.Reader) chan string {
          line := make(chan string)
           go func(quit chan int) {
                    defer close(line)
                    scan := bufio.NewScanner(r)
                    for scan.Scan() {
                    select {
                       case <- quit:
                          return
                       default:
                          s := scan.Text()
                          line <- s
                    }
                    }
                }(quit)
               return line
    }

            stdIn := scanReader(quit, os.Stdin) 
            conIn := scanReader(quit, c) 
        for {
            select {
                    case <-quit:
                        return
                    case l <- stdIn:
                        _, e := fmt.Fprintf(c, l)
                        if e != nil {
                           quit <- 1
                           return
                        }
                    case l <- conIn:
                        fmt.Println(l)
                    }
        }
func scanReader(退出chan int,r io.Reader)chan字符串{
行:=制造(成串)
go func(退出chan int){
延迟关闭(行)
扫描:=bufio.NewScanner(r)
对于scan.scan(){
挑选{

如果您两次调用
copy
(也不要使用
copy
,因为这是一个bultin函数),并且每一个都关闭同一个频道。在关闭之前,您正在检查
quit
,因为您在之后调用
copy
。您通常不想关闭stdin,也可能不想关闭stdout。为什么在for循环中调用
copy
,因为一旦完成,它就完成了?嗨,我不明白,如果您有代码示例没有hey将不胜感激。感谢您的简化,现在就实施它-关于您的后一段:是否愿意给出一个示例代码?我认为这是我上周一直在努力解决的一个问题的核心解决方案!事实上,如果您知道
net.Conn
从未期望通过半封闭连接返回数据,将
c.Close
放在goroutine中,这就足够了。你的解决方案让我吃惊。多个梯度。我正试图从Stdin error if语句中调用另一个函数(函数B)-if err!=nil{B()}…-不知怎的,一个日志。从函数B打印出来,但是像fmt。Print NOT这样的东西出现了。-知道它为什么会这样吗?@tatoe:嗯,你已经关闭了Stdout,所以你不能再向Stdout写信了。如果你想继续向Stdout写信,那就不要关闭它。这解决了我整整一周的工作。我知道该如何感谢你了!