2

好的,所以我有这段代码

func registerDomain(domainName string, n int) bool {
    //building the request here 
    resp, errr := client.Do(r)
    if errr != nil { 
        if n == 1 {
            return false
        }
        registerDomain(domainName, n-1)
    }

    bodyBytes, err2 := ioutil.ReadAll(resp.Body)
    if err2 == nil {
        resp.Body.Close()
        //handle bodyBytes
        //if the response is how it should be return true, if it's not call the function again with n-1
    } else { //if there is an error reading the response
        resp.Body.Close()
        if n == 1 {
            return false
        }
        registerDomain(domainName, n-1)
    }
    return false //it should never reach this line
}

解释:

我用一个参数 n(比如说 5)调用该函数,该参数表示如果出现问题,该函数将重试的次数。每次出现问题时,我都会使用 n-1 进行递归调用,因此当它达到 n=1 时,它会放弃并返回 false。这段代码在实践中运行良好,它完成了它应该做的事情,有时当响应不正确时,它会递归地调用自己,并且它会在第二次(或第三次、第四次......)时间运行。当问题在 n=1 之前没有解决时,它返回 false。

问题:

这是一段大代码的一部分,它应该运行大约 17 个小时,当它尝试从正文中读取时,它有时会在这一行上出现恐慌:

bodyBytes, err2 := ioutil.ReadAll(resp.Body)

它说恐慌:运行时错误:无效的内存地址或零指针取消引用。现在我知道这可能意味着它正在尝试从不存在的 resp.Body 中读取,但Go 文档明确指出当 err 为 nil 时,resp 始终包含非 nil resp.Body

因此,在我看来,这可能与递归调用有关。对我来说唯一有意义的是这种情况:假设 errr 不是 nil (这意味着 resp.Body 不存在),所以它进入 if errr != nil 并且因为 n!= 1,它会再次调用自己,n=4。假设这次一切正常,第二个函数对第一个函数返回 true,但第一个函数继续执行并尝试从不存在的 resp.Body 中读取。这会引起恐慌,我们在这里......

所以,我需要的是一个确切地知道递归函数是如何工作的人,如果不是这样,我可以在阅读它之前以某种方式检查 resp.Body 的存在,或者有帮助的东西。

不管怎么说,多谢拉!:)

更新:你很好,我的代码不再恐慌,我也没有。非常感谢!(我不确定这是否是更新的地方)

4

3 回答 3

3

改变

registerDomain(domainName, n-1)

return registerDomain(domainName, n-1)

这样外部函数将不会继续执行,也不会从 nil 主体中读取。

于 2015-01-13T15:10:20.673 回答
2

registerDomain()函数内部有 2 个地方可以调用它自己。

如果client.Do()失败 ( errr != nil),您将registerDomain()再次调用。有时它会返回,当它返回时,您的代码将继续执行,尝试从中读取,resp.Body但这很可能是nil因为errr != nil.

以下是您应该如何处理它:

无论何时进行递归调用,都不应在该调用返回时让代码继续执行,而应返回它返回的值,如下所示:

return registerDomain(domainName, n-1)

选择

您尝试解决的问题可以在不使用for语句递归的情况下解决。变体for甚至会更清晰、更简单。

修改您的registerDomain()函数以在它调用自身的每个点返回 false,并使用此循环重试 5 次:

var result bool
for i := 0; i < 5; i++ {
    result = registerDomain(domainName) // You don't need to pass n anymore
    if result {
        break
    }
}

if result {
    fmt.Println("Domain registered successfully!")
} else {
    fmt.Println("Failed to register domain!")
}
于 2015-01-13T15:11:13.463 回答
1

您需要在第一次 registerDomain 调用后返回,否则当您对 registerDomain 的调用完成时,您会在 ioutil.ReadAll 上感到恐慌

//building the request here 
resp, errr := client.Do(r)
if errr != nil { 
    if n == 1 {
        return false
    }
    return registerDomain(domainName, n-1)
}
于 2015-01-13T15:10:16.010 回答