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

我如何处理Goroutines的恐慌?

闻人宜
2023-03-14

我对golang还很陌生。所以,请把剑给我(如果可能的话)。

我试图通过学习这里的教程从web上获取数据

在问之前,我看了很多参考资料,比如:

  1. 博客延迟,恐慌和恢复
  2. 在Goroutines中处理恐慌
  3. how-show-i-write-goroutine

还有几个,但我想不通。

以下是你不想去游乐场的代码(原因尚不为人所知):

// MakeRequest : Makes requests concurrently
func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    start := time.Now()
    resp, err := http.Get(url)
    defer func() {
        resp.Body.Close()
        wg.Done()
            if r := recover(); r != nil {
                fmt.Println("Recovered in f", r)
            }
    }()
    if err != nil {
        fmt.Println(err)
        panic(err)
    }
    secs := time.Since(start).Seconds()
    body, _ := ioutil.ReadAll(resp.Body)

    ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
}

func main() {
    var wg sync.WaitGroup
    output := []string{
        "https://www.facebook.com",
        "",
    }
    start := time.Now()
    ch := make(chan string)
    for _, url := range output {
        wg.Add(1)
        go MakeRequest(url, ch, &wg)
    }

    for range output {
        fmt.Println(<-ch)
    }
    fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

我将代码修改为(假设)处理goroutine中的错误,如下所示(go-playground):

func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    start := time.Now()
    resp, err := http.Get(url)

    if err == nil {
        secs := time.Since(start).Seconds()
        body, _ := ioutil.ReadAll(resp.Body)

        ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
        // fmt.Println(err)
        // panic(err)
    }
    defer wg.Done()
}

回答后,我将代码更改为此,它成功地移除了chan死锁,但是现在我需要在main中处理此问题:

func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
    defer wg.Done()
    start := time.Now()
    resp, err := http.Get(url)

    if err == nil {
        secs := time.Since(start).Seconds()
        body, _ := ioutil.ReadAll(resp.Body)

        ch <- fmt.Sprintf("%.2f elapsed with response length: %d %s", secs, len(body), url)
        // fmt.Println(err)
        // panic(err)
    }
    // defer resp.Body.Close()
    ch <- fmt.Sprintf("")
}

但现在我陷入了僵局。

感谢和问候。
Temporarya
(阿戈朗·努比)

共有1个答案

陆宾白
2023-03-14

您正确地使用了恢复。您有两个问题:

>

  • 您错误地使用了panic。只有在出现编程错误时才应该惊慌。避免使用恐慌,除非你相信删除程序是对所发生的事情的合理反应。在这种情况下,我只会返回错误,而不是panic。

    你在恐慌中惊慌失措。正在发生的情况是,您首先在panic(err)执行panish操作。然后在defer函数中,在resp.body.close()处进行panising。当http.get返回错误时,它返回一个nil响应。这意味着resp.body.close()作用于一个nil值。

    func MakeRequest(url string, ch chan<- string, wg *sync.WaitGroup) {
        defer wg.Done()
        start := time.Now()
        resp, err := http.Get(url)
        if err != nil {
            //handle error without panicing
        }
        // there was no error, so resp.Body is guaranteed to exist.
        defer resp.Body.Close()
        ...
    

    操场

    func main() {
        var wg sync.WaitGroup
        output := []string{
            "https://www.facebook.com",
            "",
        }
        start := time.Now()
        ch := make(chan string)
        for _, url := range output {
            wg.Add(1)
            go MakeRequest(url, ch, &wg)
        }
    
        go func() {
            wg.Wait()
            close(ch)
        }()
    
        for val := range ch {
            fmt.Println(val)
        }
        fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
    }
    

  •  类似资料:
    • 如何从围棋套路中出现的恐慌中恢复过来。以下是游乐场的链接:https://play.golang.org/p/lkvkuxmhjhi

    • 我目前正在考虑如何编写测试来检查给定的代码是否出现了恐慌?我知道Go使用来捕捉恐慌,但与Java代码不同的是,您不能真正指定在发生恐慌时应该跳过哪些代码或您有哪些代码。所以如果我有一个函数: 我真的不知道是否发生了恐慌,我们恢复了,或者函数是否根本没有恐慌。如何指定在没有恐慌的情况下跳过哪些代码,以及在出现恐慌的情况下执行哪些代码?我如何检查我们是否从恐慌中恢复过来?

    • 我试图找到正确的方法来使用工人围棋例程产生的结果,同时在所有工作完成后优雅地退出结果循环。为了说明,我做了以下示例。我的真实世界案例与这个例子略有不同,因为我不知道每个工人围棋例程将返回多少“工作”,显然这些for循环执行固定数量的结果(5)。 我是goroutines和频道的新手,但以下是我所理解的基本租户; 只有发送者才能关闭频道 在通道上执行将继续,直到通道关闭 这个例子是死锁,因为范围循环

    • 我有一些图像只包含数字和分号。 您可以在这里看到更多:https://imgur.com/a/54dsl6h 它们在我看来非常干净和简单,但Tesseract认为它们是空的“页面”()。 我怎么做才能让Tesseract更好地识别角色?

    • 问题内容: 我想派生一个go进程并获取新进程的ID,但是我在或库中看到的只是启动一个新进程。 问题答案: 您应该从包装中获取。 请注意,这是在根本不使用任何线程的情况下发明的,并且一个进程中始终只有一个执行线程,因此分叉是安全的。使用Go,情况完全不同,因为它大量使用OS级线程来为其goroutine调度提供动力。 现在,在Linux上未经修饰的子进程将在所有活动线程中只有一个线程(在父进程中调用

    • 问题内容: 我正在尝试将对象保存到数据库中,但是抛出错误。 问题出在表格内,用一个复选框表示。如果未选中该复选框,则显然不传递任何内容。这就是错误被排除的地方。 我如何正确处理并捕获此异常? 该行是 问题答案: 使用MultiValueDict的方法。这在标准字典中也存在,并且是一种在不存在默认值的情况下获取值的方法。 通常,