当前位置: 首页 > 知识库问答 >
问题:

如何在出错时在内部干净地停止goroutines

倪德业
2023-03-14

所有的,

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并将通道作为函数参数发送--但是如果程序是高度分层的,这就变得不确定了。例如,我可能有一个由几个函数组成的goroutine,每个函数处理不同的tcp会话--我不想在子程序中添加额外的通道参数,以便在调用堆栈中上下推进通道,以防我必须设置完成标志。此外,还有一个问题是,在设置done标志之后,goroutine会发生什么,以及如何在调用方中处理它。

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

我看到了恐慌功能,但我不确定这样做的副作用。您能在不影响主程序的情况下panic()退出goroutine,或者有没有一种方法可以智能地短路goroutine,而不产生副作用,也许返回类似异常的东西,带有堆栈跟踪和错误?或者建议什么方法来干净地错误处理像这样的分层程序?

非常感谢你的帮助--我是golang的新手,这可能表明了。

艾德

共有1个答案

鲁鸿
2023-03-14

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)
        }()
    }
}
 类似资料:
  • 我终于在我的程序中实现了Thread.interrupt()而不是Thread.stop()。然而,我不确定我做得好不好。 我有一个类,它扩展了Thread并声明了几个方法。每个方法都抛出InterruptedException(每个方法都执行I/O密集型操作,其中一些需要几分钟才能完成,因此我没有使用线程安全标志,因为该标志直到操作完成后才会被检查)。我还在这些方法的几个地方添加了以下代码来抛出

  • 问题内容: 我最近一直在尝试使用logback,并且一直在Eclipse内部直接运行示例。当我这样做时,我注意到- 即使在我的静态方法结束之后(从Java驱动程序类内部),应用程序仍在运行。 最终,我确定Logback正在管理自己的线程,这些线程即使在我的主应用程序退出后仍保持活动状态。我四处搜寻一些解决方案,发现这是从Java内部显式关闭Logback的一种方式: 这真的是彻底关闭登录的唯一方法

  • 问题内容: 一个没有stopServer功能的RMI服务器,可以正常工作。 每当被调用异常中的stopServer()抛出时 这是堆栈跟踪 即使我使用清理服务对象,情况也一样 有人可以提出一种干净的方法来停止服务器,这还会释放端口以供重用。 问题答案: 您需要存储结果并取消导出结果。目前,您正在尝试取消导出存根。

  • 问题内容: 这是我的问题:我有一个对话框,其中包含一些用户可以更改的参数(例如,通过微调器)。每次更改这些参数之一时,我都会启动一个线程以根据新的参数值更新3D视图。如果在第一个线程正在工作时用户更改了另一个值(或通过单击微调箭头多次再次更改了相同的值),我想中止第一个线程(以及3D视图的更新)并启动一个新线程具有最新的参数值。 我该怎么做? PS:我的线程的方法中没有循环,因此检查标志不是一个选

  • 我是Android Studio的新手,所以我面临一些问题。我正在寻找一种如何干净地项目的方法。 在Eclipse中,我会做<代码>项目-

  • 怎么停止这个计时器,知道吗? 我想重置计时器在每个查询,但它继续。每个新的查询都会添加新的计时器。这个怎么解决?