我正在尝试从给定单词开始为我正在使用BFS的给定单词抓取相关单词,并在dictionary.com上搜索每个相关单词
我尝试了没有并发性的这段代码,它工作得很好,但是要花很多时间,因此尝试使用go例程,但是我的代码在第一次迭代后就卡住了。 BFS的第一级工作正常,但是在第二级中挂起!
package main
import (
"fmt"
"github.com/gocolly/colly"
"sync"
)
var wg sync.WaitGroup
func buildURL(word string) string {
return "https://www.dictionary.com/browse/" + string(word)
}
func get(url string) []string {
c := colly.NewCollector()
c.IgnoreRobotsTxt = true
var ret []string
c.OnHTML("a.css-cilpq1.e15p0a5t2", func(e *colly.HTMLElement) {
ret = append(ret, string(e.Text))
})
c.Visit(url)
c.Wait()
return ret
}
func threading(c chan []string, word string) {
defer wg.Done()
var words []string
for _, w := range get(buildURL(word)) {
words = append(words, w)
}
c <- words
}
func main() {
fmt.Println("START")
word := "jump"
maxDepth := 2
//bfs
var q map[string]int
nq := map[string]int {
word: 0,
}
vis := make(map[string]bool)
queue := make(chan []string, 5000)
for i := 1; i <= maxDepth; i++ {
fmt.Println(i)
q, nq = nq, make(map[string]int)
for word := range q {
if _, ok := vis[word]; !ok {
wg.Add(1)
vis[word] = true
go threading(queue, word)
for v := range queue {
fmt.Println(v)
for _, w := range v {
nq[w] = i
}
}
}
}
}
wg.Wait()
close(queue)
fmt.Println("END")
}
输出:
START
1
[plunge dive rise upsurge bounce hurdle fall vault drop advance upturn inflation increment spurt boost plummet skip bound surge take]
永远挂在这里,计数器= 2未打印!
可以在https://www.dictionary.com/browse/jump处查看相关单词。
答案 0 :(得分:0)
仅在缓冲区已满时发送到缓冲的通道块。 当缓冲区为空时接收块。
因此,在这种情况下,您将创建一个长度为5000的缓冲通道。
for i := 1; i <= maxDepth; i++ {
fmt.Println(i)
q, nq = nq, make(map[string]int)
for word := range q { // for each word
if _, ok := vis[word]; !ok { // if not visited visit
wg.Add(1) // add a worker
vis[word] = true
go threading(queue, word) // fetch in concurrent manner
for v := range queue { // <<< blocks here when queue is empty
fmt.Println(v)
for _, w := range v {
nq[w] = i
}
}
}
}
}
如您所见,我已经在代码中进行了注释,在第一次迭代之后,for循环将阻塞直到通道为空。在这种情况下,在获取jump
之后,它会向数组发送对应的类似单词,但是之后,由于for循环因 zerkems 而阻塞,因此您将无法进行下一次迭代(i = 2)。您最终可以关闭通道以结束for循环中的阻塞。但是,由于您使用同一个通道来覆盖多个goroutine,因此如果您从多个goroutine中关闭它,将会感到恐慌。
为克服这一点,我们可以提出一个不错的解决方法。
首先,我们需要计算未访问的单词,然后我们可以迭代很多时间
vis := make(map[string]bool)
queue := make(chan []string, 5000)
for i := 1; i <= maxDepth; i++ {
fmt.Println(i)
q, nq = nq, make(map[string]int)
unvisited := 0
for word := range q {
if _, ok := vis[word]; !ok {
vis[word] = true
unvisited++
wg.Add(1)
go threading(queue, word)
}
}
wg.Wait() // wait until jobs are done
for j := 0; j < unvisited; j++ { // << does not block as we know how much
v := <-queue // we exactly try to get unvisited amount
fmt.Println(v)
for _, w := range v {
nq[w] = i
}
}
}
在这种情况下,我们只是在计算获得结果所需的最小迭代次数。此外,您还可以看到我已经将for循环移到了外部,并使用原始循环向工作人员添加了单词。它将要求提取所有单词,并在以下循环中等待以无阻塞方式完成任务。
后期循环一直等到所有工作程序完成为止。在下一次迭代之后,可以达到BFS的工作和下一个级别。
摘要
希望这会有所帮助。