演講稿:Go Concurrency Patternsgolang
Youtube視頻web
做者:Rob Pikeshell
練習題目:谷歌搜索:一個虛擬框架服務器
PPT從43頁開始:https://talks.golang.org/2012/concurrency.slide#43app
Google函數接受一個查詢並返回一個結果切片(只是字符串)。Google連續調用網頁、圖片和視頻搜索,並將它們附加到搜索結果切片中。框架
代碼以下:ide
package main import ( "fmt" "math/rand" "time" ) var ( Web = fakeSearch("web") Image = fakeSearch("image") Video = fakeSearch("video") ) type Result string type Search func(query string) Result func fakeSearch(kind string) Search { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result(fmt.Sprintf("%s result for %q\n", kind, query)) } } func Google(query string) (results []Result) { results = append(results, Web(query)) results = append(results, Image(query)) results = append(results, Video(query)) return } func main() { rand.Seed(time.Now().UnixNano()) start := time.Now() results := Google("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed) }
運行結果以下:函數
[web result for "golang" image result for "golang" video result for "golang" ] 153.365484ms
同時運行網頁、圖像和視頻搜索,並等待全部結果。沒有鎖,沒有條件變量,沒有回調。code
代碼以下,關注Google函數。視頻
package main import ( "fmt" "math/rand" "time" ) var ( Web = fakeSearch("web") Image = fakeSearch("image") Video = fakeSearch("video") ) type Result string type Search func(query string) Result func fakeSearch(kind string) Search { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result(fmt.Sprintf("%s result for %q\n", kind, query)) } } func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- Web(query) } () go func() { c <- Image(query) } () go func() { c <- Video(query) } () for i := 0; i < 3; i++ { result := <-c results = append(results, result) } return } func main() { rand.Seed(time.Now().UnixNano()) start := time.Now() results := Google("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed) }
不要等待緩慢的服務器。沒有鎖,無條件變量,沒有回調。經過select的超時實現,須要把time.After定義的超時通道放在for循環外層。
package main import ( "fmt" "math/rand" "time" ) var ( Web = fakeSearch("web") Image = fakeSearch("image") Video = fakeSearch("video") ) type Result string type Search func(query string) Result func fakeSearch(kind string) Search { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result(fmt.Sprintf("%s result for %q\n", kind, query)) } } func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- Web(query) } () go func() { c <- Image(query) } () go func() { c <- Video(query) } () timeout := time.After(80 * time.Millisecond) for i := 0; i < 3; i++ { select { case result := <-c: results = append(results, result) case <-timeout: fmt.Println("timed out") return } } return } func main() { rand.Seed(time.Now().UnixNano()) start := time.Now() results := Google("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed) }
內容從48頁到51頁。
使用複製的搜索服務器減小尾部延遲。一樣沒有鎖,沒有條件變量,沒有回調。
問:咱們如何避免由於服務器運行緩慢而丟棄結果?
答: 複製服務器。 向多個副本發送請求,並使用第一個響應。
代碼以下:
package main import ( "fmt" "math/rand" "time" ) var ( Web1 = fakeSearch("web") Web2 = fakeSearch("web") Image1 = fakeSearch("image") Image2 = fakeSearch("image") Video1 = fakeSearch("video") Video2 = fakeSearch("video") ) type Result string type Search func(query string) Result func fakeSearch(kind string) Search { return func(query string) Result { time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) return Result(fmt.Sprintf("%s result for %q\n", kind, query)) } } func Google(query string) (results []Result) { c := make(chan Result) go func() { c <- First(query, Web1, Web2) } () go func() { c <- First(query, Image1, Image2) } () go func() { c <- First(query, Video1, Video2) } () timeout := time.After(80 * time.Millisecond) for i := 0; i < 3; i++ { select { case result := <-c: results = append(results, result) case <-timeout: fmt.Println("timed out") return } } return } func First(query string, replicas ...Search) Result { c := make(chan Result) searchReplica := func(i int) { c <- replicas[i](query) } for i := range replicas { go searchReplica(i) } return <-c } func main() { rand.Seed(time.Now().UnixNano()) start := time.Now() results := Google("golang") elapsed := time.Since(start) fmt.Println(results) fmt.Println(elapsed) }
執行結果以下:
[image result for "golang" web result for "golang" video result for "golang" ] 53.605273ms