如何有效关闭两个goroutine?

时间:2018-12-27 16:04:17

标签: 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.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)
            }
        }
    }()
}

此错误为panic: close of closed channel 我想终止两个go例程,然后通常继续执行另一个功能。我在做什么错了?

2 个答案:

答案 0 :(得分:3)

您不能在一个频道上多次调用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

答案 1 :(得分:0)

也可能是这样

 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)
                    }
        }