2017-03-09 105 views
1

我是新来的去,我看到了这个练习一些解决方案,但我认为他们是复杂的...为golang旅游的简单解决方案的WebCrawler行使

在我的解决方案一切似乎很简单,但我有死锁错误。我不知道如何正确关闭通道并在主块内部停止循环。有没有简单的方法来做到这一点?

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

也许我需要从'fakeFetcher'提取有用的信息,就像没有更多的URL取,我需要关闭频道? –

回答

1

你可以用sync.WaitGroup

  1. 解决这个问题,您可以开始在不同的够程听你的渠道。
  2. WaitGroup会协调你有多少个goroutines。

wg.Add(1)说我们要开始新的goroutine。

wg.Done()说goroutine完成。

wg.Wait() blocks goroutine,直到所有开始的goroutines尚未完成。

这3种方法可以让你协调goroutines。

Go playground link

PS。你可能有兴趣在sync.RWMutex为您的SafeCache

+0

非常感谢!很优雅的解决方案 –

相关问题