這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
代碼:
SafeRun加鎖設定url的已訪問
通過
for i:=0; i < len(urls); i++{<-sub_quit}父go線程等待子go線程結束
package mainimport ("fmt")type Fetcher interface {// Fetch 返回 URL 的 body 內容,並且將在這個頁面上找到的 URL 放到一個 slice 中。Fetch(url string) (body string, urls []string, err error)}var lockx = make(chan int,1) func SafeRun(f func()){<-lockxf()lockx <-1}var visited map[string]bool = make(map[string]bool) // Crawl 使用 fetcher 從某個 URL 開始遞迴的爬取頁面,直到達到最大深度。func Crawl(url string, depth int, fetcher Fetcher, quit chan int) {// TODO: 並行的抓取 URL。// TODO: 不重複抓取頁面。 // 下面並沒有實現上面兩種情況:defer func(){quit<-1}()if depth <= 0 || visited[url] {return}body, urls, err := fetcher.Fetch(url)SafeRun(func(){visited[url]=true})if err != nil {fmt.Println(err)return}fmt.Printf("found: %s %q\n", url, body)sub_quit := make(chan int,len(urls))for _, u := range urls {Crawl(u, depth-1, fetcher,sub_quit)}for i:=0; i < len(urls); i++{<-sub_quit}return}func main() {lockx<-1quit := make(chan int,1)Crawl("http://golang.org/", 4, fetcher,quit)fmt.Println(<-quit)}// fakeFetcher 是返回若干結果的 Fetcher。type fakeFetcher map[string]*fakeResulttype fakeResult struct {body stringurls []string}func (f fakeFetcher) Fetch(url string) (string, []string, error) {if res, ok := f[url]; ok {return res.body, res.urls, nil}return "", nil, fmt.Errorf("not found: %s", url)}// fetcher 是填充後的 fakeFetcher。var fetcher = fakeFetcher{"http://golang.org/": &fakeResult{"The Go Programming Language",[]string{"http://golang.org/pkg/","http://golang.org/cmd/",},},"http://golang.org/pkg/": &fakeResult{"Packages",[]string{"http://golang.org/","http://golang.org/cmd/","http://golang.org/pkg/fmt/","http://golang.org/pkg/os/",},},"http://golang.org/pkg/fmt/": &fakeResult{"Package fmt",[]string{"http://golang.org/","http://golang.org/pkg/",},},"http://golang.org/pkg/os/": &fakeResult{"Package os",[]string{"http://golang.org/","http://golang.org/pkg/",},},}