閱讀Go語言官方博客系列
Go語言併發模式
這是閱讀Go語言官方博客系列的第一篇文章。Go語言官方的內容特別多,並且很有指向性的解決了我們遇到的大多數問題。
本文的內容是上個月在油管看了一個2012年的 Go 團隊在 Google I/O 的演講視頻,然後在B站找到了這個視頻的熟肉。有趣的是我截屏整理後才發現官方公佈了 幻燈片。
另外,本文僅做整理,無教程性內容。
好了,讓我們進入正題。
資料蒐集
原視頻是英文演講。
B站的視頻是國內 Up 主 Capricornwqh上傳的熟肉。
幻燈片是 Go 語言官方,在 go-blog 發佈的 slide。
-
原視頻 : Google I/O 2012 - Go Concurrency Patterns - YouTube(需科學上網)
-
幻燈片:Go Concurrency Patterns-concurrency.slide(需科學上網)
幻燈片截屏整理
(多圖預警)
代碼整理
幻燈片結束之後,我們來整理代碼
demo01
// demo01:
package main
import (
"math/rand"
"fmt"
"time"
)
func main() {
boring("boring!")
}
func boring(msg string) {
for i := 0; ; i++ {
fmt.Println(msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
demo02
// demo02
package main
import (
"fmt"
"time"
"math/rand"
)
func main() {
go boring("boring!")
}
func boring(msg string) {
for i := 0; ; i++ {
fmt.Println(msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
demo03
// demo03
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
go boring("boring!")
fmt.Println("I`m Listening...")
time.Sleep(2 * time.Second)
fmt.Println("You are boring! I`m leaving!")
}
func boring(msg string) {
for i := 0; ; i++ {
fmt.Println(msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
demo04
// demo04
// Using channels
// An aside about buffered channels
// Note for exports: Go channels can also be created with a buffer.
// Buffering removes synchronization.
// Buffering makes them more like Erlang`s mailboxes.
// Buffered channels can be important for some problems
// but they are more subtle to reason about
// We won`t need them today.
// The Go approach
// Don`t communicate by sharing memory, sharing memory by communicating.
// 不要使用內存共享的方式通信,使用通信來完成內存共享。
package main
import (
"fmt"
"time"
"math/rand"
)
func main() {
c := make(chan, string)
go boring("boring!", c)
for i := 0; i < 5; i++ {
fmt.Printf("You say: %q\n", <-c)
}
fmt.Println("You are boring! I`m leaving!")
}
func boring(msg string, c chan string) {
for i := 0; ; i++ {
c <- fmt.Sprint("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}
demo05
// demo05
// Generator: function that returns a channel
// Channels are first-class values, just like strings or integers.
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
c := boring("boring!")
for i := 0; i < 5; i++ {
fmt.Printf("You say: %q\n", <-c)
}
fmt.Println("You are boring! I`m leaving!")
}
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
demo06
// demo06
// Channels as a handle on service
// Our boring function returns a channel that let us communicate with the boring service it provides.
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
joe := boring("Joe")
ann := boring("Ann")
for i := 0; i < 5; i++ {
fmt.Println(<-joe)
fmt.Println(<-ann)
}
fmt.Println("You are boring! I`m leaving!")
}
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
demo07
// demo 07
// Multiplexing
package main
import "fmt"
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
fmt.Println("You`re both boring; I`m leaving")
}
// fanIn
// input1\input2是兩個僅接收channel
// fanIn 返回值也是一個僅接受Channel
// 拿出 僅接受Channel 的值 的方法是 <-[僅接收channel變量名]
func fanIn(input1, input2 <-chan string) <-chan string {
fmt.Printf("input 1:%v \nInput2: %v\n", input1, input2)
c := make(chan string)
go func() {
for {
c <- <-input1
}
}()
go func() {
for {
c <- <-input2
}
}()
return c
}
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
}
}()
return c
}
demo08
// demo08
// Reastoring sequencing
package main
import (
"fmt"
"math/rand"
"time"
)
// Message msg wait
type Message struct {
msg string
wait chan bool
}
func fanIn(input1, input2 <-chan Message) <-chan Message {
fmt.Printf("input 1:%v \nInput2: %v\n", input1, input2)
c := make(chan Message)
go func() {
for {
c <- <-input1
}
}()
go func() {
for {
c <- <-input2
}
}()
return c
}
func boring(msg string) <-chan Message {
c := make(chan Message)
waitFor := make(chan bool)
go func() {
for i := 0; ; i++ {
c <- Message{fmt.Sprintf("%s,%d", msg, i), waitFor}
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
<-waitFor
}
}()
return c
}
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 5; i++ {
msg1 := <-c
fmt.Println(msg1.msg)
msg2 := <-c
fmt.Println(msg2.msg)
msg1.wait <- true
msg2.wait <- true
}
fmt.Println("You`re both boring; I`m leaving")
}
demo09
// demo09
// Fan-in using select
// Rewrite our original fanIn function.
// Only one goroutine is needed.
package main
import (
"fmt"
"math/rand"
"time"
)
// Message msg wait
type Message struct {
msg string
wait chan bool
}
func fanIn(input1, input2 <-chan Message) <-chan Message {
fmt.Printf("input 1:%v \nInput2: %v\n", input1, input2)
c := make(chan Message)
go func() {
for {
select {
case s := <-input1:
c <- s
case s := <-input2:
c <- s
}
}
}()
return c
}
func boring(msg string) <-chan Message {
c := make(chan Message)
waitFor := make(chan bool)
go func() {
for i := 0; ; i++ {
c <- Message{fmt.Sprintf("%s,%d", msg, i), waitFor}
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
<-waitFor
}
}()
return c
}
func main() {
c := fanIn(boring("Joe"), boring("Ann"))
for i := 0; i < 5; i++ {
msg1 := <-c
fmt.Println(msg1.msg)
msg2 := <-c
fmt.Println(msg2.msg)
msg1.wait <- true
msg2.wait <- true
}
fmt.Println("You`re both boring; I`m leaving")
}
demo10
// demo10
// Timeout using select
// The time.After function returns a channel that blocks
// for the specified duration.
// After the interval, the channel delivers the current time, once.
package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s: %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1020)) * time.Millisecond)
}
}()
return c
}
func main() {
c := boring("Joe")
for {
select {
case s := <-c:
fmt.Println(s)
case <-time.After(1 * time.Second):
fmt.Println("You`re too slow...")
return
}
}
}
demo11
// demo11
// Timeout for whole conversation using select
// Create the timer once, outside the loop, to time out the entire conversation.
// (In the previous program, we had a timeout for each message.)
package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
c <- fmt.Sprintf("%s: %d", msg, i)
time.Sleep(time.Duration(rand.Intn(1020)) * time.Millisecond)
}
}()
return c
}
func main() {
c := boring("Joe")
timeout := time.After(3 * time.Second)
for {
select {
case s := <-c:
fmt.Println(s)
case <-timeout:
fmt.Println("You`re too slow...")
return
}
}
}
demo12
// demo12
// Qiut Channel
package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string, quit chan bool) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
select {
case c <- fmt.Sprintf("%s: %d", msg, i):
// do nothing
case <-quit:
fmt.Println("Oh! You Stop Me!")
return
}
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
func main() {
quit := make(chan bool)
c := boring("Joe", quit)
for i := rand.Intn(10); i >= 0; i-- {
fmt.Println(<-c)
}
quit <- true
}
demo13
// demo13
// Recive on quit channel
// How do we Konw it`s finished?
// Wait for it to tell us it`s done: recive onthe quit channel
package main
import (
"fmt"
"math/rand"
"time"
)
func boring(msg string, quit chan string) <-chan string {
c := make(chan string)
go func() {
for i := 0; ; i++ {
select {
case c <- fmt.Sprintf("%s: %d", msg, i):
// do nothing
case <-quit:
cleanup()
// fmt.Println("boring: Oh! You Stop Me!")
quit <- "See you!"
return
}
time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
}
}()
return c
}
func cleanup() {
fmt.Println("clean somthings")
}
func main() {
quit := make(chan string)
c := boring("Joe", quit)
for i := rand.Intn(10); i >= 0; i-- {
fmt.Println(<-c)
}
quit <- "Bye!"
fmt.Printf("Joe says: %q\n", <-quit)
}
demo14
// demo14
// Daisy-chain
package main
import "fmt"
func f(left, right chan int) {
left <- 1 + <-right
}
func main() {
const n = 100000
leftmost := make(chan int)
right := leftmost
left := leftmost
for i := 0; i < n; i++ {
right = make(chan int)
go f(left,right)
left = right
}
go func (c chan int) {
c <- 2
}(right)
fmt.Println(<-leftmost)
}
google-search-00
// demo - Google Search 00
// Google Search: A fake framework
package main
import (
"fmt"
"math/rand"
"time"
)
var (
web = fakeSearch("web")
image = fakeSearch("image")
video = fakeSearch("video")
)
// Result type
type Result string
// Search fake
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))
}
}
// Google ,
func Google(query string) Result {
return Result(fmt.Sprintf("%s%s%s", web(query), image(query), video(query)))
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
result := Google("google")
elapsed := time.Since(start)
fmt.Println(result, elapsed)
}
google-search-01
// demo - Google Search 01
// Google Search 1.0
// Synchronous
// The Google function takes a query and returns a slice of Results(which are just strings)
// Google invokes Web, Image and Video searches serially, appending them to the results slice.
package main
import (
"fmt"
"math/rand"
"time"
)
var (
web = fakeSearch("web")
image = fakeSearch("image")
video = fakeSearch("video")
)
// Result type
type Result string
// Search fake
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))
}
}
// Google ,
func Google(query string) (results []Result) {
results = append(results, web(query))
results = append(results, image(query))
results = append(results, video(query))
return results
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
result := Google("google")
elapsed := time.Since(start)
fmt.Println(result)
fmt.Println(elapsed)
}
google-search-02
// demo - Google Search 02
// Google Search 2.0
// Run the Web, Image and Video searches concurrently, and wait for all results.
// No locks, No condition variables, No callbacks
package main
import (
"fmt"
"math/rand"
"time"
)
var (
web = fakeSearch("web")
image = fakeSearch("image")
video = fakeSearch("video")
)
// Result type
type Result string
// Search fake
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))
}
}
// Google ,
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 results
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
result := Google("google")
elapsed := time.Since(start)
fmt.Println(result)
fmt.Println(elapsed)
}
google-search-03
// demo - Google Search 03
// Google Search 2.1
// Don`t wait for slow servers.
// No locks, No condition variables, No callbacks
package main
import (
"fmt"
"math/rand"
"time"
)
var (
web = fakeSearch("web")
image = fakeSearch("image")
video = fakeSearch("video")
)
// Result type
type Result string
// Search fake
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))
}
}
// Google ,
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("time out")
return
}
}
return results
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
result := Google("google")
elapsed := time.Since(start)
fmt.Println(result)
fmt.Println(elapsed)
}
google-search-04
// demo - Google Search 04
// Google Search 3.0
// Q: How do we avoid discarding results from slow servers?
// A: Replicate the servers. Send requests to multiple replicas, and use the first response.
// Reduce tail latency using replicated search servers.(使用複製的搜索服務器減少尾部延遲。)
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")
)
// Result 定義返回值
type Result string
// Search 搜索服務
type Search func(query string) Result
// fakeSearch 虛擬的搜索服務
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))
}
}
// First query: 查詢字串 replicas: 服務器的複製
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
}
// Google 聚合的Google搜索服務
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("time out")
return
}
}
return results
}
func main() {
rand.Seed(time.Now().UnixNano())
start := time.Now()
result := Google("google")
elapsed := time.Since(start)
fmt.Println(result)
fmt.Println(elapsed)
}
Panda張向北© 2018-2020 版權所有, 採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議許可,代碼使用 MIT 協議開源。