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

http.Client和goroutines的不可预测结果,go,goroutine,Go,Goroutine,我是Golang的新手,尝试构建一个系统,从一组URL获取内容,并使用正则表达式提取特定行。当我用goroutine包装代码时,问题就开始了。我得到了不同数量的正则表达式结果,许多获取的行都是重复的 max_routines := 3 sem := make(chan int, max_routines) // to control the number of working routines var wg sync.WaitGroup ch_content := make(chan str

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

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(".")
    }
}

因此,lena.feedbacks有时给出130,有时给出139,a.feedbacks包含重复项。如果我清除了重复项,结果的数量大约是我预期的一半,109没有重复项

您正在使用匿名go例程函数创建一个闭包。我注意到您的最终url不是:=但是=这意味着它是在闭包之外定义的。所有go例程都将访问相同的final_url值,并且存在竞争条件。一些go例程在其他go例程发出请求之前覆盖final_url,这将导致重复

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(".")
    }
}
如果您在go例程中定义了final_url,那么他们就不会互相攻击,应该按照您的预期工作


这是对你所拥有的东西的简单修复。一个更惯用的Go方法是创建一个包含要请求的url的输入通道和一个最终包含从响应中提取的任何内容的输出通道,而不是试图管理几十个Go例程的生死,而是保持一个恒定数量的Go例程,尝试清空响应输入通道。

您正在忽略错误,并在环路中断时进行数据竞争。首先尝试解决这些问题。您描述的内容听起来像是在向闭包中泄漏您认为不应该泄漏的变量。您尚未向我们提供所有代码,因此可能存在我们看不到的问题。我猜final_url是在go例程之外定义的,你正在创建一个竞赛条件,其中1或2个go例程认为他们已经将final_url设置为他们应该请求的url,但在他们发出请求之前,另一个go例程会再次设置它。然后有多个go例程请求相同的URL并生成重复的URL。这只是一个猜测,因为我们没有所有的代码。请阅读go中的并发:@CoreyOgburn这是正确的猜测。我改变了在goroutines中声明最终url的逻辑,现在我得到了可预测和正确的结果。谢谢你的帮助!如果你发布一个答案,我会把它标记为正确答案谢谢你提供更详细的答案。我将尝试重写考虑通道的逻辑