Ну после первого взгляда, вы можете сделать короче для просто используя диапазон нравится:
for urls := range UrlChannel { ... }
будет перебирать, пока канал не закрыт, и он выглядит намного лучше.
Также у Вас есть ранний возвращение в первом если вашей функции _Crawl(), так что если первое условие истинно, функция закончится, и ничего не передается в канал, так что код, что получает от этого канала будет ждать вечно.
Другая вещь, внутри вашей второй для твоих goroutines для каждого URL-адреса, но вы их не ждете, и на самом деле эти goroutines попытаются отправить что-то на закрытый канал. Похоже, что этого не происходит, потому что в этом случае код будет паниковать, для этого вы можете использовать WaitGroup.
В резюме у вас есть код с несколькими возможными условиями блокировки.
||| Super Edit |||:
Я должен написать вам, что ваш код является своего рода грязным, и решение может быть простым WaitGroup, но я боялся, что вы чувствуете себя плохо, потому что я обнаружил слишком много проблем, но если вы действительно хотите научиться как писать параллельный код, вы должны сначала подумать в коде или псевдокоде без параллелизма, а затем попытаться добавить магию.
В вашем случае то, что я вижу рекурсивное решение, поскольку URL извлекаются из HTML-документа в виде дерева, было бы что-то вроде DFS:
func crawl(url string, fetcher Fetcher) {
// if we've visited this url just stop the recursion
if store.Read(url) == true {
return
}
store.Write(url)
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Printf("not found: %s\n", url)
return // early return if there's no urls there's no reason to continue
}
fmt.Printf("found: %s %q\n", url, body)
// this part will change !!
// ...
for _, url := range urls {
crawl(url, fetcher)
}
//
}
func main() {
crawl("http://golang.org", fetcher)
}
теперь второй этап идентификации параллельного кода, Легко в этом случае, так как каждый URL может быть выбран одновременно (иногда параллельно), все, что мы должны добавить, это WaitGroup и создать goroutine для каждого URL-адреса, теперь просто нужно обновить только для для извлечения URL-адресов (это только для блок):
// this code will be in the comment: "this part will change !!"
//
// this var is just a thread-safe counter
var wg sync.WaitGroup
// set the WaitGroup counter with the len of urls slice
wg.Add(len(urls))
for _, url := range urls {
// it's very important pass the url as a parameter
// because the var url changes for each loop (url := range)
go func(u string) {
// Decrement the counter (-1) when the goroutine completes
defer wg.Done()
crawl(u, fetcher)
}(url)
}
wg.Wait() // wait for all your goroutines
// ...
Будущие соображения, может быть, вы хотите, чтобы контролировать количество goroutines (или рабочих) для этого вы должны использовать что-то вроде вентилятора в или веером, вы можете найти больше здесь: https://blog.golang.org/advanced-go-concurrency-patterns и https://blog.golang.org/pipelines
Но не бойтесь создавать тысячи goroutines в Go они very cheap
Примечание: Я не компилируется код, может быть, есть маленькая ошибка где-то :)
взаимоблокировка происходит потому, что вызов 'close' никогда не достигается. 'ok' в приеме канала будет false только тогда, когда канал был закрыт, и поэтому выполнение застряло в этом цикле. Вы должны рассмотреть возможность использования цикла диапазона (http://dave.cheney.net/2014/03/19/channel-axioms). Если вы в порядке с переработкой дизайна немного, что-то вроде этого может работать как подсказка: https://play.golang.org/p/aiuiyueyzB – abhink
да, закрыть канал в недоступном, но это не корень проблемы здесь и нет, выполнение застрянет только в случае бесконечных URL-адресов. –