http.Client和goroutines的不可预测的结果

时间:2018-06-19 21:48:08

标签: go goroutine

我是Golang的新手,他试图构建一个从一组url获取内容并使用regex提取特定行的系统。当我使用goroutines包装代码时,问题就开始了。我得到的正则表达式结果数量不同,很多提取的行都是重复的。

max_routines := 3

sem := make(chan int, max_routines) // to control the number of working routines 
var wg sync.WaitGroup
ch_content := make(chan string)

client := http.Client{}

for i:=2; ; i++ { 

    // for testing
    if i>5 {
        break
    }

    // loop should be broken if feebbacks_checstr is found in content
    if loop_break {
        break
    }

    wg.Add(1)
    go func(i int) {

        defer wg.Done()

        sem <- 1 // will block if > max_routines

        final_url = url+a.tm_id+"/page="+strconv.Itoa(i)

        resp, _ := client.Get(final_url)

        var bodyString string 

        if resp.StatusCode == http.StatusOK {
            bodyBytes, _ := ioutil.ReadAll(resp.Body)
            bodyString = string(bodyBytes)
        }

        // checking for stop word in content
        if false == strings.Contains(bodyString, feebbacks_checstr) {

            res2 = regex.FindAllStringSubmatch(bodyString,-1)
            for _,v := range res2 {
                ch_content <- v[1]
            }

        } else {
            loop_break = true
        }

        resp.Body.Close()

        <-sem

    }(i)
}


for {
    select {
        case r := <-ch_content:
            a.feedbacks = append(a.feedbacks, r) // collecting the data 
        case <-time.After(500 * time.Millisecond):
            show(len(a.feedbacks)) // < always different result, many entries in a.feedbacks are duplicates
            fmt.Printf(".")
    }
}

结果是len(a.feedbacks)有时给出130,有时139,而a.feedbacks包含重复项。如果我清理重复项,结果的数量大约是我期望值的一半(109个不重复项)

1 个答案:

答案 0 :(得分:0)

您正在通过使用匿名go例程函数来创建闭包。我注意到您的final_url不是:=,而是=,这意味着它是在闭包之外定义的。所有go例程都可以访问相同的final_url值,并且存在争用情况。一些go例程在其他go例程发出请求之前会覆盖final_url,这将导致重复。

如果您在go例程中定义final_url,那么他们就不会踩到对方的脚趾,它应该会按您的预期工作。

这是您所拥有的简单解决方案。一种更惯用的Go方法是创建一个输入通道(包含要请求的URL)和一个输出通道(最终包含您从响应中拉出的所有内容),而不是尝试管理几十个go例程,您将保持一定数量的go例程,这些例程会尝试清空输入通道。