【问题标题】:Invalid memory address using gorutine (golang)使用 gorutine (golang) 的内存地址无效
【发布时间】:2017-05-17 14:59:37
【问题描述】:

我正在使用的代码只有在我让它作为阻塞例程运行时才有效……但如果我使用go executeTheFunction,我总是会得到panic: runtime error: invalid memory address or nil pointer dereference。稍微总结一下代码就是这样。

func (c App) Index() revel.Result {
    app = c; //this is a "global" variable to be used in callBackFunction 
    options = "foo";
    client  = NewClient()
    client.RequestAsynch(options, callBackFunction);
}
func callBackFunction(message string){
    reader := bytes.NewBufferString(message)
    _, err := xmlpath.Parse( reader )
    if err != nil {
        panic(err)
    }
    flusher, ok := airshopping.Response.Out.(http.Flusher)
    if !ok {
      panic("expected http.ResponseWriter to be an http.Flusher")
    }

    fmt.Fprintf(airshopping.Response.Out, "whatever")
    flusher.Flush()
}

func (client * Client)RequestAsynch(message Message, callback func(string)) {
    Response := client.Request(message)

    message_aux := ""
    reader := bufio.NewReader(Response.Body)
    for {
        line, err := reader.ReadBytes('\n')
        if err==nil {
            message_aux = message_aux + string(line)
            if strings.Contains(message_aux, "<!-- AG-EOM -->"){
                go callback(message_aux)
                message_aux = ""
            }
        }else{fmt.Println("ERROR", err);break;}
    }
}

好的...我将解释一下。 RequestAsynch 位于不同的包中,它向其他服务器发起请求。这个其他服务器响应多个 xml...这些 xml 的主体看起来像:

<!-- Header status:ok -->
<all_the_xml_response>
<!-- AG-EOM -->

所以...当逐行循环检测到这个"&lt;!-- AG-EOM --&gt;" 是完全接收到其中一个xml 时,应该执行回调函数(使用gorutine)。这个callBack函数的任务是解析xml,经过一个解析过程,将一些数据发送回客户端的javascript。

我总是遇到的错误是:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x20 pc=0x4f8c09]

goroutine 80 [running]:
bufio.(*Writer).Write(0xc042454540, 0xc0422d4000, 0x11076, 0x12000, 0xc04202fb30, 0x4683b7, 0x11076)
    C:/Go/src/bufio/bufio.go:598 +0x149
net/http.(*response).write(0xc04234d0a0, 0x11076, 0xc0422d4000, 0x11076, 0x12000, 0x0, 0x0, 0xc0422d4000, 0x0, 0x0)
    C:/Go/src/net/http/server.go:1525 +0x157
net/http.(*response).Write(0xc04234d0a0, 0xc0422d4000, 0x11076, 0x12000, 0x0, 0x0, 0xc042824000)
    C:/Go/src/net/http/server.go:1495 +0x6b
fmt.Fprintf(0xb65e80, 0xc04234d0a0, 0xc042824000, 0x11076, 0x0, 0x0, 0x0, 0xc0421445f0, 0xb, 0xc0422f5e00)
    C:/Go/src/fmt/print.go:182 +0xb0
ndc-console-test/app/controllers/ndcmethods.GetOffers_AS(0xc042594000, 0xef7f)
    F:/Proyectos_Trabajo/work/src/ndc-console-    test/app/controllers/ndcmethods/airshopping.go:98 +0x7a1
created by github.com/open-ndc/ndc-go-sdk.(*Client).RequestAsynch
    F:/Proyectos_Trabajo/work/src/github.com/open-ndc/ndc-go-sdk/ndc_client.go:143 +0x25c
2017/05/17 16:42:56 reverseproxy.go:316: http: proxy error: dial tcp [::1]:55058: connectex:A connection can not be established because the target computer expressly denied the connection.

出现此错误后...我需要重新启动服务器,因为它不允许建立新连接。

我看到的一件事是,如果我在 callBackFunction 中删除这一行 _, err := xmlpath.Parse( reader ) ,它就可以正常工作。当然,如果我更改此行:go callback(message_aux)callback(message_aux)(没有 goroutine)它也可以正常工作,并且解析 xml 没有问题。

有谁知道如何使用 goroutines 避免这个错误的解决方案?

非常感谢。

编辑

我认为我没有很好地表达自己对其他服务器的响应方式,这是(多个)响应的外观:

<!-- Header status:ok -->
<the_xml_response_of_one_response_or_one_chunk>
<!-- AG-EOM -->
<!-- Header status:ok -->
<other_xml_response>
<!-- AG-EOM -->
<!-- Header status:ok -->
<other_more_xml_response>
<!-- AG-EOM -->

【问题讨论】:

  • http.ResponseWriter 仅在请求的生命周期内有效。在 Go 中没有理由使用这样的异步回调模式。

标签: go xml-parsing request buffer goroutine


【解决方案1】:

根据您的堆栈跟踪,问题似乎是,一旦父函数返回,HTTP 响应就被写出,当新的 goroutine 有机会尝试时,编写器不再可用。

这是net/http 设计的一部分,所以我怀疑您的RequestAsynch 无法按预期编写。为什么需要异步?每个请求都已在其自己的 goroutine 中处理。

顺便说一句,strings.NewReader 可能比bytes.NewBufferString 更合适,因为您不需要完整的缓冲区接口。

【讨论】:

  • 嗨@nothingmuch。感谢您的回答。让我解释得更好一点。问题是我没有发出多个请求......我只发出一个请求,而另一台服务器向我发送了许多 xml 响应(请查看我在问题正文中编辑的关于服务器如何响应的内容),这样,我需要为每个 xml 调用一次回调函数。如果我使用callback(message_aux) 而不是go callback(message_aux) 它可以工作。
  • 据我所知,它并没有改变任何东西,在这两种情况下,您都需要在 RequestAsynch 结束时等待,并等到您启动的所有 goroutines 完成。您可以通过使用sync.WaitGroup 来做到这一点,或者让他们通过通道发送结果而不是直接使用响应编写器,但事实仍然是在 HTTP 处理程序返回后您无法使用响应编写器
  • 你好@nothingmuch。而且...如果我在 callBackFunction 中删除此行:`_, err := xmlpath.Parse(reader)` 为什么会起作用?谢谢。
  • 可能是因为 goro 的执行速度足够快,可以在输出刷新到客户端之前写入输出。这是一种竞争条件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 1970-01-01
  • 1970-01-01
  • 2015-02-24
相关资源
最近更新 更多