在完成单个任务下发到worker,然后worker进行处理完成的阶段后。
此时需要coordinator进行任务调度处理,以此让所有空闲的worker进行并行工作
要想完成lab1此阶段的任务,需要熟悉以下Go语言的知识
- go routine
- go channel
下面以一个简单的例子进行学习演示
该程序完成一个递归爬虫爬取页面的任务(此处进行简化处理)。一个url页面下面可能会有很多个新的url,该程序需要爬取到所有的url后,才进行返回。
采用深度优先的方式进行爬取,由于子程序采用go routine的方式,所以为了防止共享数据的访问冲突。下文采用同步锁或者go channel的方式分别实现。
- 使用锁的方式实现
package main
import (
"fmt"
"sync"
)
type Fetcher struct {}
func (f Fetcher) ParseUrl(url string) (urls []string, err error) {
urls = append(urls, url)
err = nil
return
}
type fetchState struct{
mu sync.Mutex
fetched map[string]bool
}
// 使用锁实现一个递归解析url的函数
func recursion(url string, fetcher Fetcher ,f *fetchState) {
f.mu.Lock()
already := f.fetched[url]
f.fetched[url] = true
f.mu.Unlock()
if already {
return
}
fmt.Println(url)
urls, _ := fetcher.ParseUrl(url)
var wg sync.WaitGroup
for _, v := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
recursion(u, fetcher, f)
}(v)
}
wg.Wait()
}
func main() {
recursion("test", Fetcher{}, &fetchState{
fetched: make(map[string]bool, 0)
})
}
- 使用go channel的方式实现
package main
import (
"fmt"
"sync"
)
type Fetcher struct {}
func (f Fetcher) ParseUrl(url string) (urls []string, err error) {
urls = append(urls, url)
err = nil
return
}
type fetchState struct{
mu sync.Mutex
fetched map[string]bool
}
// 使用chan实现一个递归解析url的函数
func recursion2(url string, fetcher Fetcher) {
ch := make(chan []string, 0)
// ch <- []string{url} 此处会引发死锁
go func() {
ch <- []string{url}
}()
master(ch, fetcher)
}
func master(ch chan []string, fetcher Fetcher) {
fetched := make(map[string]bool, 0)
i := 0
for urls := range ch {
for _, v := range urls {
if fetched[v] == false {
go worker(ch, v, fetcher)
fetched[v] = true
i += 1
}
}
i -= 1
if i == 0 {
break
}
}
}
func worker(ch chan []string, url string, fetcher Fetcher) {
urls, _ := fetcher.ParseUrl(url)
// 简单处理,此处默认成功
ch <- urls
}
func main() {
recursion2("hanbo", Fetcher{})
}