我正在经历Go Tour,我觉得除了并发性之外,我对这门语言有很好的理解。
slide 10是一个练习,它要求读者对网络爬虫进行并行化(并使其不包含重复,但我还没有做到这一点)。
这是我到目前为止所知道的:
func Crawl(url string, depth int, fetcher Fetcher, ch chan string) {
if depth <= 0 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
ch <- fmt.Sprintln(err)
return
}
ch <- fmt.Sprintf("found: %s %q\n", url, body)
for _, u := range urls {
go Crawl(u, depth-1, fetcher, ch)
}
}
func main() {
ch := make(chan string, 100)
go Crawl("http://golang.org/", 4, fetcher, ch)
for i := range ch {
fmt.Println(i)
}
}
我的问题是,我应该把close(ch)
调用放在哪里。
如果我将一个defer close(ch)
放在Crawl
方法中的某个地方,那么程序最终会从一个衍生的goroutine写入关闭的通道,因为对Crawl
的调用将在衍生的goroutine返回之前返回。
如果我省略了对close(ch)
的调用,正如我所演示的那样,程序会在确定通道范围的主函数中死锁,因为当所有goroutines都返回时,通道永远不会关闭。
发布于 2012-11-05 06:57:43
看一看Effective Go的并行化部分,就可以得到解决方案的想法。实际上,您必须关闭函数的每个返回路径上的通道。实际上,这是defer语句的一个很好的用例:
func Crawl(url string, depth int, fetcher Fetcher, ret chan string) {
defer close(ret)
if depth <= 0 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
ret <- err.Error()
return
}
ret <- fmt.Sprintf("found: %s %q", url, body)
result := make([]chan string, len(urls))
for i, u := range urls {
result[i] = make(chan string)
go Crawl(u, depth-1, fetcher, result[i])
}
for i := range result {
for s := range result[i] {
ret <- s
}
}
return
}
func main() {
result := make(chan string)
go Crawl("http://golang.org/", 4, fetcher, result)
for s := range result {
fmt.Println(s)
}
}
与您的代码的本质区别在于,Crawl的每个实例都有自己的返回通道,而调用者函数在其返回通道中收集结果。
发布于 2016-04-23 01:14:25
我选择了一个完全不同的方向。我可能被关于使用地图的提示误导了。
// SafeUrlMap is safe to use concurrently.
type SafeUrlMap struct {
v map[string]string
mux sync.Mutex
}
func (c *SafeUrlMap) Set(key string, body string) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
c.v[key] = body
c.mux.Unlock()
}
// Value returns mapped value for the given key.
func (c *SafeUrlMap) Value(key string) (string, bool) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.v.
defer c.mux.Unlock()
val, ok := c.v[key]
return val, ok
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher, urlMap SafeUrlMap) {
defer wg.Done()
urlMap.Set(url, body)
if depth <= 0 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
for _, u := range urls {
if _, ok := urlMap.Value(u); !ok {
wg.Add(1)
go Crawl(u, depth-1, fetcher, urlMap)
}
}
return
}
var wg sync.WaitGroup
func main() {
urlMap := SafeUrlMap{v: make(map[string]string)}
wg.Add(1)
go Crawl("http://golang.org/", 4, fetcher, urlMap)
wg.Wait()
for url := range urlMap.v {
body, _ := urlMap.Value(url)
fmt.Printf("found: %s %q\n", url, body)
}
}
发布于 2016-09-22 03:18:01
O(1)在地图上查找url的时间,而不是在所有访问的url的切片上查找O(n),这将有助于最小化在临界区内花费的时间,这对于本例来说是微不足道的时间量,但将与规模相关。
WaitGroup用于阻止顶级Crawl()函数在所有子go例程完成之前返回。
func Crawl(url string, depth int, fetcher Fetcher) {
var str_map = make(map[string]bool)
var mux sync.Mutex
var wg sync.WaitGroup
var crawler func(string,int)
crawler = func(url string, depth int) {
defer wg.Done()
if depth <= 0 {
return
}
mux.Lock()
if _, ok := str_map[url]; ok {
mux.Unlock()
return;
}else{
str_map[url] = true
mux.Unlock()
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("found: %s %q %q\n", url, body, urls)
for _, u := range urls {
wg.Add(1)
go crawler(u, depth-1)
}
}
wg.Add(1)
crawler(url,depth)
wg.Wait()
}
func main() {
Crawl("http://golang.org/", 4, fetcher)
}
https://stackoverflow.com/questions/13217547
复制相似问题