我剛開始使用 golang 學習多執行緒編程,并且正在嘗試使用 BFS 遍歷撰寫多執行緒網路爬蟲,但是我無法使代碼正常作業。我得到的錯誤是fatal error: all goroutines are asleep - deadlock!
我將粘貼下面的代碼,但讓我從概念上解釋它是如何作業的:
我有一個主執行緒(主函式本身)和 N 個作業執行緒。我故意選擇使用具有固定數量作業執行緒的 BFS 方法,因為似乎使用 DFS 方法我必須為每個要抓取的新 URL 生成一個新執行緒,這可能會成為背景關系切換的巨大負擔。
我正在使用兩個渠道:
- urlsToCrawl:主執行緒將要抓取的 URL 發送到作業執行緒。
- urlsDiscovered:作業執行緒將發現的 URL 發送回 master。
這是代碼實作,我洗掉了一些不相關的細節(例如如何決議html頁面等。)
我在這里嘗試做的技巧是:我正在使用通道作為佇列來做 BFS,當佇列的大小為 0 時,無法知道是否是因為“A.真的沒有更多的 URLs 到抓取”或因為“B. 一些作業執行緒仍在作業,因此可能很快會有更多 URL 需要抓取”。因此我引入了這個count變數,基本上每當一個新的 url 被發送給 worker 進行爬取時,count都會遞增,因此當count == 0channel 為空時,這意味著“A.真的沒有更多的 URLs 可以爬取”;否則,當count > 0通道為空時,這意味著“B. 一些作業執行緒仍在作業,因此可能很快會有更多 URL 需要抓取”。
但是,正如我所提到的,這似乎不起作用,我陷入了僵局。有人能說明一下嗎?謝謝!
package main
import (
"fmt"
)
var (
count = 0 // This tracks how many worker threads are actively working right now
)
func crawlUrl(urlsToCrawl chan string, urlsDiscovered chan Pair) {
for url := range urlsToCrawl {
urls := getUrls(url) // This returns an array of string, if no URL found, it returns an empty array
urlsDiscovered <- urls
}
}
func main() {
urlsToCrawl := make(chan string)
urlsDiscovered := make(chan string[])
i := 0
for i < 8 {
go crawlUrl(urlsToCrawl, urlsDiscovered)
i
}
visited := map[string]bool{"some_seed_url": true}
count
urlsToCrawl <- "some_seed_url"
for urls := range urlsDiscovered {
count-- // One message is received by master, meaning one worker thread has finished an job item, therefore decrementing count
for _, url := range urls {
_, ok := visited[url]
if ok {
continue // This URL has been crawled before
}
visited[url] = true
count // One more work item will be sent to worker, therefore first increment count
urlsToCrawl <- url
}
if count == 0 {
close(urlsDiscovered)
close(urlsToCrawl)
break
} // else some worker must be working so let's wait to see if there is new msg coming through the channel
}
}
uj5u.com熱心網友回復:
嘗試集成WaitGroup包。
uj5u.com熱心網友回復:
您的頻道沒有緩沖
urlsToCrawl := make(chan string)
urlsDiscovered := make(chan string[])
因此,讀取或寫入通道的 goroutine 將阻塞,直到另一側的 goroutine 執行相反的操作。
因此,您啟動了 8 個crawlUrlgoroutine,它們在讀取時都阻塞urlsToCrawl,這意味著main可以在阻塞之前發送 8 個 url。該crawlUrl夠程被阻塞,直到main從讀取urlsDiscovered。所以如果你有超過 8 個 URL,所有的 goroutine 都在互相等待(死鎖)。
解決方案是使用容量不太可能超過的緩沖通道:
urlsToCrawl := make(chan string, 1000)
urlsDiscovered := make(chan string[], 100)
如果您希望在極端情況下仍可能超出通道的容量,您可以執行非阻塞操作,例如,如果通道已滿而不是阻塞,您可以丟棄發現的 URL。
select {
case: urlsDiscovered <- urls:
// on success (url written)
default:
// channel is full, can't write without blocking
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/407115.html
標籤:
