0

全部,

我正在编写一个涉及具有多个故障点的 tcp 流量的程序,并且我希望能够在错误条件下顺利退出 goroutine 而不会产生编码开销。

这是一些伪代码:

func main() {

    l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+ CONN_PORT)

    for {                                                                                                                                                                              
        // Listen for an incoming connection.                                                                                                                                   
        conn, err := l.Accept()                                                                                                                                                        
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())                                                                                                                                  
            os.Exit(1)
        }

        done_flag := make(chan bool, 1)

        // Handle connections in a new goroutine.                                                                                                                                      
        go func() { 
            conn.Write([]byte("string1\n"))
            conn.Write([]byte("string2\n"))
            ...
        }()
    }
}

现在,我要避免的是以下带有连接语句的代码,我将代码包装在 goroutine 内的错误处理中(如下所示):

    go func() {
        if (_err := _send_ack(conn, "string1\n"); _err != nil {
            done_flag <- true
        }
        if (_err := _send_ack(conn, "string2\n"); _err != nil {
            done_flag <- true
        }
    }()

相反,如果存在连接问题,我宁愿将整个事情短路,然后立即退出 goroutine 并出现错误 - 我宁愿不必担心我如何构建代码。也许我可以进一步包装 _send_ack 并将通道作为函数参数发送 - 但如果程序变得高度分层,那将变得不确定。例如,我可能有一个由多个 func 组成的 goroutine,每个 func 处理不同的 tcp 会话 - 我不想在我的子例程中乱扔额外的通道参数来在调用堆栈上下传播通道以防万一我必须设置一个完成标志。此外,还有一个问题是在设置完成标志之后 goroutine 会发生什么以及如何在调用者中处理它。

如果我在 python、perl 或 C++ 中工作,我会抛出一个异常,该异常已附加到发生错误的堆栈跟踪,然后在调用者中处理此错误。但是由于 golang 没有异常,我希望有一种方法可以在不实际退出主程序的情况下停止 goroutine 冷态 - 即:设置一个通道以产生相关错误,然后在该点停止执行。

我看到了恐慌功能,但我不确定它的副作用。你能在不影响主程序的情况下从 goroutine 中恐慌(),或者有没有办法智能地短路 goroutine 而没有副作用,也许返回类似于异常的东西,带有堆栈跟踪和错误?或者,什么是干净的错误处理这样的分层程序的建议方法?

非常感谢您的帮助 - 我是 golang 的新手,它可能会显示出来。

埃德

4

1 回答 1

0

golang 建议使用显式错误而不是使用隐式异常。

// for code simplicity
func doSendACKImpl(conn net.Conn) error {
    if err := _send_ack(conn, "string1\n"); err != nil {
        return err
    }

    if err := _send_ack(conn, "string2\n"); err != nil {
        return err
    }

    return nil
}

func main() {
    l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+ CONN_PORT)

    for {
        // Listen for an incoming connection.
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }

        // can change to self defined ResponseType, here use error for demo
        workRes := make(chan error, 1)
        go func() {
            // return write back to channel
            workRes <- doSendACKImpl(conn)
        }()

        select {
        // read result back
        case resError := <-workRes:
            fmt.Printf("meet error %s", resError)
        }
    }
}

为了获得更多的并发能力,使用更多的通道缓冲区大小,并将处理结果处理程序移动到另一个 goroutine

func main() {
    l, _ := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT)

    // more result buffer size
    const workSize int = 100

    // can change to self defined ResponseType, here use error for demo
    workResBuffer := make(chan error, workSize)

    // goroutine collect result
    go func() {
        // get all result from worker responses
        for resError := range workResBuffer {
            fmt.Printf("meet error %s", resError)
        }
    }()

    for {
        // Listen for an incoming connection.
        conn, err := l.Accept()
        if err != nil {
            fmt.Println("Error accepting: ", err.Error())
            os.Exit(1)
        }

        // TODO: limit the goroutine number
        go func() {
            // return write back to channel
            workResBuffer <- doSendACKImpl(conn)
        }()
    }
}
于 2020-11-02T05:50:11.990 回答