使用goroutine的正确方法是什么?

时间:2013-07-31 09:11:03

标签: go goroutine

我需要对每个请求应用一些测试,并根据测试结果激活响应。如果其中一个测试失败,我需要立即发送响应,否则我等待所有测试成功完成。我想用并发性进行测试。

现在,我这样做(简化):

func handler_request_checker(w http.ResponseWriter, r *http.Request) {
    done := make(chan bool)
    quit := make(chan bool)
    counter := 0

    go TestOne(r,done,quit)
    go TestTwo(r,done,quit)
    ..............
    go TestTen(r,done,quit)


    for {
        select {
            case <- quit:
                fmt.Println("got quit signal")
                return
            case <- done:
                counter++
                if counter == 10 {
                    fmt.Println("All checks passed succesfully")
                    return
                }
        }
    }

func main() {
    http.HandleFunc("/", handler_request_checker)
    http.ListenAndServe()
}

goroutine之一的例子:

func TestOne(r *http.Request, done,quit chan bool) {
    ip,_,ok := net.SplitHostPort(r.RemoteAddr)
    if ok == nil {
        for _,item := range BAD_IP_LIST {
            if strings.Contains(ip,item) {
                quit <- true
                return
            }
        }
        done <- true
        return
    } else {
        quit <- true
        return
    }
}

问题是,在我退出信号后,goroutines没有“释放内存”。我想这是因为在chanel中有所作为。 我是GO的新手,所以也许我错误地使用它们?

例如,当我开始加载检查http_load -parallel 10 -seconds 10时,该goroutine很容易吃掉100多MB的RAM,并且不会将其返回给系统。在下次检查时,它会多吃100多MB,依此类推。

如果我在没有go的情况下进行测试(逐步),程序在任何负载检查时都不会超过10-15mb。

1 个答案:

答案 0 :(得分:1)

你的猜测是正确的。您正在使用同步通道,这意味着发送方和接收方都必须可用才能传输值。

一旦您的handler_request_checker函数获得退出信号,它就会停止检索donequit频道的任何值。进一步TestOneTestTwo等goroutines将在尝试发送结果时被阻止。更糟糕的是,他们将永远留在记忆中,因为他们还在运行,因为他们尚未传输他们的结果。

解决问题的一种方法是为donequit使用缓冲(异步)通道。例如:

done := make(chan bool, 10)
quit := make(chan bool, 10)

如果您为10次测试使用10的缓冲区大小,那么所有goroutine都能够发送结果,即使没有可用的读者也是如此。所有goroutine都将干净地退出,并且一旦所有goroutine退出,通道(可能包含一些未读结果)将被收集垃圾。