Go語言併發模式-閱讀Go語言官方博客系列

在這裏插入圖片描述

閱讀Go語言官方博客系列

Go語言併發模式

這是閱讀Go語言官方博客系列的第一篇文章。Go語言官方的內容特別多,並且很有指向性的解決了我們遇到的大多數問題。
本文的內容是上個月在油管看了一個2012年的 Go 團隊在 Google I/O 的演講視頻,然後在B站找到了這個視頻的熟肉。有趣的是我截屏整理後才發現官方公佈了 幻燈片

另外,本文僅做整理,無教程性內容。

好了,讓我們進入正題。

資料蒐集

原視頻是英文演講。

B站的視頻是國內 Up 主 Capricornwqh上傳的熟肉。

幻燈片是 Go 語言官方,在 go-blog 發佈的 slide。

幻燈片截屏整理

(多圖預警)

Screen Shot 2020-04-26 at 09.57.44

Screen Shot 2020-04-26 at 10.01.38

Screen Shot 2020-04-26 at 10.03.14

Screen Shot 2020-04-26 at 10.04.55

Screen Shot 2020-04-26 at 10.05.16

Screen Shot 2020-04-26 at 10.05.58

Screen Shot 2020-04-26 at 10.06.26

Screen Shot 2020-04-26 at 10.08.07

Screen Shot 2020-04-26 at 10.09.40

Screen Shot 2020-04-26 at 10.09.52

Screen Shot 2020-04-26 at 10.24.33

Screen Shot 2020-04-26 at 10.26.57

Screen Shot 2020-04-26 at 10.28.42

Screen Shot 2020-04-26 at 10.29.27

Screen Shot 2020-04-26 at 10.31.04

Screen Shot 2020-04-26 at 10.35.10

Screen Shot 2020-04-26 at 10.36.41

Screen Shot 2020-04-26 at 10.37.00

Screen Shot 2020-04-26 at 10.38.40

Screen Shot 2020-04-26 at 10.39.29

Screen Shot 2020-04-26 at 10.39.57

Screen Shot 2020-04-26 at 10.42.44

Screen Shot 2020-04-26 at 10.43.17

Screen Shot 2020-04-26 at 10.45.04

Screen Shot 2020-04-26 at 10.46.35

Screen Shot 2020-04-26 at 10.48.37

Screen Shot 2020-04-26 at 10.48.46

Screen Shot 2020-04-26 at 10.51.44

Screen Shot 2020-04-26 at 10.52.14

Screen Shot 2020-04-26 at 10.54.26

Screen Shot 2020-04-26 at 10.55.05

Screen Shot 2020-04-26 at 11.01.17

Screen Shot 2020-04-26 at 11.02.28

Screen Shot 2020-04-26 at 11.02.57

Screen Shot 2020-04-26 at 11.03.28

Screen Shot 2020-04-26 at 11.04.10

Screen Shot 2020-04-26 at 11.05.25

Screen Shot 2020-04-26 at 11.07.34

Screen Shot 2020-04-26 at 11.08.35

Screen Shot 2020-04-26 at 11.09.57

Screen Shot 2020-04-26 at 11.11.23

Screen Shot 2020-04-26 at 11.12.12

Screen Shot 2020-04-26 at 11.20.06

Screen Shot 2020-04-26 at 11.20.14

Screen Shot 2020-04-26 at 11.21.05

Screen Shot 2020-04-26 at 11.22.03

Screen Shot 2020-04-26 at 11.23.23

Screen Shot 2020-04-26 at 11.25.04

Screen Shot 2020-04-26 at 11.25.42

Screen Shot 2020-04-26 at 11.26.06

Screen Shot 2020-04-26 at 11.29.36

代碼整理

幻燈片結束之後,我們來整理代碼

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 協議開源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章