http.Client 和 goroutines 的不可预测的结果
Unpredictable results with http.Client and goroutines
我是 Golang 的新手,正在尝试构建一个系统,从一组 url 中获取内容并使用正则表达式提取特定行。当我用 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 没有重复)
您正在使用匿名 go 例程函数创建闭包。我注意到你的 final_url
不是 :=
而是 =
这意味着它是在闭包之外定义的。所有 go 例程都可以访问相同的 final_url
值,并且存在竞争条件。一些 go 例程在其他 go 例程发出请求之前覆盖 final_url
,这将导致重复。
如果您在 go 例程中定义 final_url
那么它们就不会踩到对方的脚趾,它应该会按您预期的那样工作。
这是对您所拥有的内容的简单修复。一种更惯用的 Go 方法是创建一个输入通道(包含要请求的 URL)和一个输出通道(最终包含您从响应中提取的任何内容),而不是试图管理的生死几十个 go routines 你会保持一定数量的 go routines 试图清空输入通道。
我是 Golang 的新手,正在尝试构建一个系统,从一组 url 中获取内容并使用正则表达式提取特定行。当我用 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 没有重复)
您正在使用匿名 go 例程函数创建闭包。我注意到你的 final_url
不是 :=
而是 =
这意味着它是在闭包之外定义的。所有 go 例程都可以访问相同的 final_url
值,并且存在竞争条件。一些 go 例程在其他 go 例程发出请求之前覆盖 final_url
,这将导致重复。
如果您在 go 例程中定义 final_url
那么它们就不会踩到对方的脚趾,它应该会按您预期的那样工作。
这是对您所拥有的内容的简单修复。一种更惯用的 Go 方法是创建一个输入通道(包含要请求的 URL)和一个输出通道(最终包含您从响应中提取的任何内容),而不是试图管理的生死几十个 go routines 你会保持一定数量的 go routines 试图清空输入通道。