2017-03-09 24 views
1

を行使ウェブクローラgolangツアーのためのソリューション...シンプルな私の解決策は、私が行くことに新たなんだと私はこの練習のためにいくつかの解決策を見たが、私は、彼らが複雑だと思う

すべてがシンプルなようだが、私が持っていますデッドロックエラー。私はどのように正常にチャネルを閉じ、メインブロック内のループを停止するかを把握することはできません。これを行う簡単な方法はありますか? 1が提供された/すべての助けを

Solution on Golang playground

ありがとう!

package main 

import (
    "fmt" 
    "sync" 
) 

type Fetcher interface { 
    // Fetch returns the body of URL and 
    // a slice of URLs found on that page. 
    Fetch(url string) (body string, urls []string, err error) 
} 

type SafeCache struct { 
    cache map[string]bool 
    mux sync.Mutex 
} 

func (c *SafeCache) Set(s string) { 
    c.mux.Lock() 
    c.cache[s] = true 
    c.mux.Unlock() 
} 

func (c *SafeCache) Get(s string) bool { 
    c.mux.Lock() 
    defer c.mux.Unlock() 
    return c.cache[s] 
} 

var (
    sc = SafeCache{cache: make(map[string]bool)} 
    errs, ress = make(chan error), make(chan string) 
) 

// Crawl uses fetcher to recursively crawl 
// pages starting with url, to a maximum of depth. 
func Crawl(url string, depth int, fetcher Fetcher) { 
    if depth <= 0 { 
     return 
    } 

    var (
     body string 
     err error 
     urls []string 
    ) 

    if ok := sc.Get(url); !ok { 
     sc.Set(url) 
     body, urls, err = fetcher.Fetch(url) 
    } else { 
     err = fmt.Errorf("Already fetched: %s", url) 
    } 

    if err != nil { 
     errs <- err 
     return 
    } 

    ress <- fmt.Sprintf("found: %s %q\n", url, body) 
    for _, u := range urls { 
     go Crawl(u, depth-1, fetcher) 
    } 
    return 
} 

func main() { 
    go Crawl("http://golang.org/", 4, fetcher) 
    for { 
     select { 
     case res, ok := <-ress: 
      fmt.Println(res) 
      if !ok { 
       break 
      } 
     case err, ok := <-errs: 
      fmt.Println(err) 
      if !ok { 
       break 
      } 
     } 
    } 
} 

// fakeFetcher is Fetcher that returns canned results. 
type fakeFetcher map[string]*fakeResult 

type fakeResult struct { 
    body string 
    urls []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 is a populated 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/", 
     }, 
    }, 
} 
+0

多分私はフェッチするURLがなくなり、チャンネルを閉じる必要があるというような 'fakeFetcher'から有用な情報を抽出する必要がありますか? –

答えて

1

あなたはあなたが別のゴルーチンであなたのチャンネルを聴き始めることができますsync.WaitGroup

  1. でこれを解決することができます。
  2. WaitGroupは、あなたが持っているゴルーチンの数を調整します。

wg.Add(1)は、新しいゴルーチンを開始すると言います。

wg.Done()は、ゴルーチンが終了したことを示します。

wg.Wait()は、開始されたすべてのゴルーチンがまだ終了していない限り、ゴルーチンをブロックします。

この3つの方法では、ゴルーチンを調整できます。

Go playground link

PS。 sync.RWMutexに興味があるかもしれませんSafeCache

+0

ありがとう!非常にエレガントなソリューション! –

関連する問題