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

标签 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()
}

其中一个协程的例子:

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 在我退出信号后不会释放内存。我想这是因为 done chanel 中有一些东西。 我是 GO 的新手,所以我可能用错了它们?

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

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

最佳答案

你猜对了。您正在使用同步 channel ,这意味着发送方和接收方都必须可用才能传输值。

一旦您的 handler_request_checker 函数收到退出信号,它就会停止检索 donequit channel 的任何值。此外,TestOneTestTwo 等 goroutine 在尝试发送结果时将被阻止。更糟糕的是,它们将永远留在内存中,因为它们仍在运行,因为它们尚未传输结果。

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

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

如果您对 10 个测试使用大小为 10 的缓冲区,那么所有 goroutine 都能够发送它们的结果,即使没有可用的读取器。所有 goroutines 都将干净地退出,并且一旦所有 goroutines 退出, channel (可能包含一些未读结果)将被垃圾收集。

关于go - 什么是使用 goroutine 的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17966188/

相关文章:

sql - Go 中数据库迁移的正确策略

go - Go中数据的分层抽象

select - 为什么golang select语句不随机选择一个case

docker - 使用 golang 将 docker 镜像拉取到本地注册表

algorithm - 带有等待时间的for循环内的Golang End Goroutine

go - 在 new func 中运行 goroutine 或不在 recover 之间的区别

go - 在选择中支持一种通信(chan)

go - 给定代码的执行流程将如何? go 例程在这里将如何执行?

go - 具有 channel 参数的函数中的死锁

go - filepath.Walk() 返回 0xc08402f180