├── 1-boring ├── example1 │ └── boring.go ├── example2 │ └── boring.go └── example3 │ └── boring.go ├── 2-channel └── example1 │ └── channel.go ├── 3-generator ├── example1 │ └── generator.go └── example2 │ └── generator.go ├── 4-multiplexing ├── example1 │ └── multiplexing.go └── sequencing │ └── sequencing.go ├── 5-select ├── example1 │ └── select.go ├── example2 │ └── select.go ├── example3 │ └── select.go ├── example4 │ └── select.go ├── example5 │ └── select.go └── example6 │ └── select.go ├── 6-search ├── example1 │ └── search.go ├── example2 │ └── search.go ├── example3 │ └── search.go ├── example4 │ └── search.go └── example5 │ └── search.go └── README.md /1-boring/example1/boring.go: -------------------------------------------------------------------------------- 1 | // Program loops five times printing a message. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | boring("boring!") 12 | } 13 | 14 | func boring(msg string) { 15 | for i := 0; i < 5; i++ { 16 | fmt.Println(msg, i) 17 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /1-boring/example2/boring.go: -------------------------------------------------------------------------------- 1 | /* 2 | We execute a separate Goroutine to run the boring function. 3 | 4 | The program terminates immediately because when main terminates, 5 | the program terminates. 6 | */ 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "math/rand" 12 | "time" 13 | ) 14 | 15 | func main() { 16 | // Use a goroutine. 17 | go boring("boring!") 18 | 19 | // Program will terminate immediately. 20 | } 21 | 22 | func boring(msg string) { 23 | for i := 0; ; i++ { 24 | fmt.Println(msg, i) 25 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /1-boring/example3/boring.go: -------------------------------------------------------------------------------- 1 | /* 2 | We execute a separate Goroutine to run the boring function. 3 | 4 | The program terminates immediately because when main terminates, 5 | the program terminates. 6 | 7 | We give the Goroutine two seconds to perform some work. 8 | */ 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "math/rand" 14 | "time" 15 | ) 16 | 17 | func main() { 18 | // Use a goroutine. 19 | go boring("boring!") 20 | 21 | fmt.Println("I'm listening.") 22 | time.Sleep(2 * time.Second) 23 | fmt.Println("You're boring: I'm leaving.") 24 | 25 | // Program will terminate immediately. 26 | } 27 | 28 | func boring(msg string) { 29 | for i := 0; ; i++ { 30 | fmt.Println(msg, i) 31 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /2-channel/example1/channel.go: -------------------------------------------------------------------------------- 1 | /* 2 | When the main function executes <-c, it will wait for a value to be sent. 3 | 4 | When the boring function executes c <- value, it waits for a receiver to 5 | be ready. 6 | 7 | A sender and receiver must both be ready to play their part in the 8 | communication. Otherwise we wait until they are. 9 | 10 | Thus channels both communicate and synchronize. 11 | 12 | Don't communicate by sharing memory, share memory by communicating. 13 | */ 14 | package main 15 | 16 | import ( 17 | "fmt" 18 | "math/rand" 19 | "time" 20 | ) 21 | 22 | func main() { 23 | // Unbuffered Channel of strings. 24 | c := make(chan string) 25 | 26 | go boring("boring!", c) 27 | 28 | for i := 0; i < 5; i++ { 29 | // Read From Channel - Blocking. 30 | fmt.Printf("You say: %q\n", <-c) // Receive expression is just a value. 31 | } 32 | 33 | fmt.Println("You're boring: I'm leaving.") 34 | } 35 | 36 | func boring(msg string, c chan string) { 37 | for i := 0; ; i++ { 38 | // Write to Channel. 39 | c <- fmt.Sprintf("%s %d", msg, i) // Expression to be sent can be any suitable value. 40 | 41 | // The write does not return until the read from main is complete. 42 | 43 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /3-generator/example1/generator.go: -------------------------------------------------------------------------------- 1 | // Generator: Function that returns a channel 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | c := boring("boring!") // Function returning a channel. 12 | 13 | for i := 0; i < 5; i++ { 14 | fmt.Printf("You say: %q\n", <-c) 15 | } 16 | 17 | fmt.Println("You're boring: I'm leaving.") 18 | } 19 | 20 | func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings. 21 | c := make(chan string) 22 | 23 | go func() { // Launch the goroutine from inside the function. Function Literal. 24 | for i := 0; ; i++ { 25 | c <- fmt.Sprintf("%s %d", msg, i) 26 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 27 | } 28 | }() 29 | 30 | return c // Return the channel to the caller. 31 | } 32 | -------------------------------------------------------------------------------- /3-generator/example2/generator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Generator: Function that returns a channel 3 | 4 | The boring function returns a channel that lets us communicate with the 5 | boring service it provides. 6 | 7 | We can have more instances of the service. 8 | */ 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "math/rand" 14 | "time" 15 | ) 16 | 17 | func main() { 18 | joe := boring("Joe") 19 | ann := boring("Ann") 20 | 21 | for i := 0; i < 5; i++ { 22 | fmt.Println(<-joe) // Joe and Ann are blocking each other. 23 | fmt.Println(<-ann) // waiting for a message to read. 24 | } 25 | 26 | fmt.Println("You're boring: I'm leaving.") 27 | } 28 | 29 | func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings. 30 | c := make(chan string) 31 | 32 | go func() { // Launch the goroutine from inside the function. Function Literal. 33 | for i := 0; ; i++ { 34 | c <- fmt.Sprintf("%s %d", msg, i) 35 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 36 | } 37 | }() 38 | 39 | return c // Return the channel to the caller. 40 | } 41 | -------------------------------------------------------------------------------- /4-multiplexing/example1/multiplexing.go: -------------------------------------------------------------------------------- 1 | /* 2 | Multiplexing: Let whosoever is ready to talk, talk. 3 | 4 | The fanIn function fronts the other channels. Goroutines that are ready to talk 5 | can independently talk without Blocking the other Goroutines. The FanIn channel 6 | receives all messages for processing. 7 | 8 | Decouples the execution between the different Goroutines. 9 | 10 | Joe --- 11 | \ 12 | ----- FanIn --- Independent Messages Displayed 13 | / 14 | Ann --- 15 | */ 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "math/rand" 21 | "time" 22 | ) 23 | 24 | func main() { 25 | c := fanIn(boring("Joe"), boring("Ann")) 26 | 27 | for i := 0; i < 10; i++ { 28 | fmt.Println(<-c) // Display any message received on the FanIn channel. 29 | } 30 | 31 | fmt.Println("You're boring: I'm leaving.") 32 | } 33 | 34 | func fanIn(input1, input2 <-chan string) <-chan string { 35 | c := make(chan string) // The FanIn channel 36 | 37 | go func() { // This Goroutine will receive messages from Joe. 38 | for { 39 | c <- <-input1 // Write the message to the FanIn channel, Blocking Call. 40 | } 41 | }() 42 | 43 | go func() { // This Goroutine will receive messages from Ann 44 | for { 45 | c <- <-input2 // Write the message to the FanIn channel, Blocking Call. 46 | } 47 | }() 48 | 49 | return c 50 | } 51 | 52 | func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings. 53 | c := make(chan string) 54 | 55 | go func() { // Launch the goroutine from inside the function. Function Literal. 56 | for i := 0; ; i++ { 57 | c <- fmt.Sprintf("%s %d", msg, i) 58 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 59 | } 60 | }() 61 | 62 | return c // Return the channel to the caller. 63 | } 64 | -------------------------------------------------------------------------------- /4-multiplexing/sequencing/sequencing.go: -------------------------------------------------------------------------------- 1 | /* 2 | Restoring Sequencing 3 | 4 | Send a channel on a channel, making Goroutine wait its turn 5 | 6 | Receive all messages, then enable them again by sending on a private channel 7 | 8 | Each speaker must wait for a go-ahead. 9 | 10 | Joe: Send and Wait --- --- Joe: Display and Wait 11 | \ / 12 | ----- FanIn --- 13 | / \ 14 | Ann: Send and Wait --- --- Ann: Display then Release Waits 15 | */ 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "math/rand" 21 | "time" 22 | ) 23 | 24 | // Message contains a channel for the reply. 25 | type Message struct { 26 | str string 27 | wait chan bool // Acts as a signaler 28 | } 29 | 30 | func main() { 31 | 32 | c := fanIn(boring("Joe"), boring("Ann")) 33 | 34 | for i := 0; i < 10; i++ { 35 | msg1 := <-c // Waiting on someone (Joe) to talk 36 | fmt.Println(msg1.str) 37 | 38 | msg2 := <-c // Waiting on someone (Ann) to talk 39 | fmt.Println(msg2.str) 40 | 41 | msg1.wait <- true // Joe can run again 42 | msg2.wait <- true // Ann can run again 43 | } 44 | 45 | fmt.Println("You're boring: I'm leaving.") 46 | } 47 | 48 | func fanIn(input1, input2 <-chan Message) <-chan Message { 49 | c := make(chan Message) // The FanIn channel. 50 | 51 | go func() { // This Goroutine will receive messages from Joe. 52 | for { 53 | c <- <-input1 // Write the message to the FanIn channel, Blocking Call. 54 | } 55 | }() 56 | 57 | go func() { // This Goroutine will receive messages from Ann. 58 | for { 59 | c <- <-input2 // Write the message to the FanIn channel, Blocking Call. 60 | } 61 | }() 62 | 63 | return c 64 | } 65 | 66 | func boring(msg string) <-chan Message { // Returns receive-only (<-) channel of strings. 67 | c := make(chan Message) 68 | waitForIt := make(chan bool) // Give main control over our execution. 69 | 70 | go func() { // Launch the goroutine from inside the function. Function Literal. 71 | for i := 0; ; i++ { 72 | c <- Message{fmt.Sprintf("%s %d", msg, i), waitForIt} 73 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 74 | 75 | <-waitForIt // Block until main tells us to go again. 76 | } 77 | }() 78 | 79 | return c // Return the channel to the caller. 80 | } 81 | -------------------------------------------------------------------------------- /5-select/example1/select.go: -------------------------------------------------------------------------------- 1 | /* 2 | Select is a control structure that is unique to concurrency. 3 | 4 | The reason channels and Goroutines are built into the language. 5 | 6 | Like a switch but each case is a communication: 7 | -- All channels are evaluated 8 | -- Selection blocks until one communication can proceed, which then does. 9 | -- If multiple can proceed, select choose pseudo-randomly. 10 | -- Default clause, if present, executes immediately if no channel is ready. 11 | 12 | Multiplexing: Let whosoever is ready to talk, talk. 13 | 14 | The fanIn function fronts the other channels. Goroutines that are ready to talk 15 | can independently talk without Blocking the other Goroutines. The FanIn channel 16 | receives all messages for processing. 17 | 18 | Decouples the execution between the different Goroutines. 19 | 20 | Joe --- 21 | \ 22 | ----- FanIn --- Independent Messages Displayed 23 | / 24 | Ann --- 25 | */ 26 | package main 27 | 28 | import ( 29 | "fmt" 30 | "math/rand" 31 | "time" 32 | ) 33 | 34 | func main() { 35 | c := fanIn(boring("Joe"), boring("Ann")) 36 | 37 | for i := 0; i < 10; i++ { 38 | fmt.Println(<-c) // Display any message received on the FanIn channel. 39 | } 40 | 41 | fmt.Println("You're boring: I'm leaving.") 42 | } 43 | 44 | func fanIn(input1, input2 <-chan string) <-chan string { 45 | c := make(chan string) // The FanIn channel 46 | 47 | go func() { // Now using a select and only one Goroutine 48 | for { 49 | select { 50 | case s := <-input1: 51 | c <- s 52 | 53 | case s := <-input2: 54 | c <- s 55 | } 56 | } 57 | }() 58 | 59 | return c 60 | } 61 | 62 | func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings. 63 | c := make(chan string) 64 | 65 | go func() { // Launch the goroutine from inside the function. Function Literal. 66 | for i := 0; ; i++ { 67 | c <- fmt.Sprintf("%s %d", msg, i) 68 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 69 | } 70 | }() 71 | 72 | return c // Return the channel to the caller. 73 | } 74 | -------------------------------------------------------------------------------- /5-select/example2/select.go: -------------------------------------------------------------------------------- 1 | /* 2 | Timeout Using Select 3 | 4 | The time.After function returns a channel that blocks for the specified duration. 5 | After the interval, the channel delivers the current time, once. 6 | 7 | The select is giving the boring routine 800ms to respond. This will be an endless 8 | loop if boring can perform its work under 800ms every time. 9 | 10 | */ 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "math/rand" 16 | "time" 17 | ) 18 | 19 | func main() { 20 | c := boring("Joe") 21 | 22 | for { 23 | select { 24 | case s := <-c: 25 | fmt.Println(s) 26 | case <-time.After(800 * time.Millisecond): // This is reset on every iteration. 27 | fmt.Println("You're too slow.") 28 | return 29 | } 30 | } 31 | } 32 | 33 | func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings. 34 | c := make(chan string) 35 | 36 | go func() { // Launch the goroutine from inside the function. Function Literal. 37 | for i := 0; ; i++ { 38 | c <- fmt.Sprintf("%s %d", msg, i) 39 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 40 | } 41 | }() 42 | 43 | return c // Return the channel to the caller. 44 | } 45 | -------------------------------------------------------------------------------- /5-select/example3/select.go: -------------------------------------------------------------------------------- 1 | /* 2 | Timeout Using Select 3 | 4 | Create the timer once, outside the loop, to time out the entire conversation. 5 | (In the previous program, we had a timeout for each message) 6 | 7 | This time the program will terminate after 5 seconds 8 | */ 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "math/rand" 14 | "time" 15 | ) 16 | 17 | func main() { 18 | c := boring("Joe") 19 | timeout := time.After(5 * time.Second) // Terminate program after 5 seconds. 20 | 21 | for { 22 | select { 23 | case s := <-c: 24 | fmt.Println(s) 25 | case <-timeout: 26 | fmt.Println("You're too slow.") 27 | return 28 | } 29 | } 30 | } 31 | 32 | func boring(msg string) <-chan string { // Returns receive-only (<-) channel of strings. 33 | c := make(chan string) 34 | 35 | go func() { // Launch the goroutine from inside the function. Function Literal. 36 | for i := 0; ; i++ { 37 | c <- fmt.Sprintf("%s %d", msg, i) 38 | time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) 39 | } 40 | }() 41 | 42 | return c // Return the channel to the caller. 43 | } 44 | -------------------------------------------------------------------------------- /5-select/example4/select.go: -------------------------------------------------------------------------------- 1 | /* 2 | Quit Channel 3 | 4 | You can turn this around and tell Joe to stop when we're tired of listening to him. 5 | */ 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "math/rand" 11 | ) 12 | 13 | func main() { 14 | quit := make(chan bool) 15 | c := boring("Joe", quit) 16 | 17 | for i := rand.Intn(10); i >= 0; i-- { 18 | fmt.Println(<-c) 19 | } 20 | 21 | quit <- true 22 | fmt.Println("EXIT") 23 | } 24 | 25 | func boring(msg string, quit chan bool) <-chan string { // Returns receive-only (<-) channel of strings. 26 | c := make(chan string) 27 | 28 | go func() { // Launch the goroutine from inside the function. Function Literal. 29 | for i := 0; ; i++ { 30 | select { 31 | case c <- fmt.Sprintf("%s %d", msg, i): 32 | // Do Nothing 33 | case <-quit: 34 | fmt.Println("Quiting") 35 | return 36 | } 37 | } 38 | }() 39 | 40 | return c // Return the channel to the caller. 41 | } 42 | -------------------------------------------------------------------------------- /5-select/example5/select.go: -------------------------------------------------------------------------------- 1 | /* 2 | Receive on Quit Channel 3 | 4 | How do we know it's finished? Wait for it to tell us it's done: receive on the quit 5 | channel 6 | */ 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "math/rand" 12 | ) 13 | 14 | func main() { 15 | quit := make(chan string) 16 | c := boring("Joe", quit) 17 | 18 | for i := rand.Intn(10); i >= 0; i-- { 19 | fmt.Println(<-c) 20 | } 21 | 22 | quit <- "Bye!" 23 | fmt.Printf("Joe Says: %q\n", <-quit) 24 | } 25 | 26 | func boring(msg string, quit chan string) <-chan string { // Returns receive-only (<-) channel of strings. 27 | c := make(chan string) 28 | 29 | go func() { // Launch the goroutine from inside the function. Function Literal. 30 | for i := 0; ; i++ { 31 | select { 32 | case c <- fmt.Sprintf("%s %d", msg, i): 33 | // Do Nothing. 34 | case <-quit: 35 | cleanup() 36 | quit <- "See you!" 37 | return 38 | } 39 | } 40 | }() 41 | 42 | return c // Return the channel to the caller. 43 | } 44 | 45 | func cleanup() { 46 | fmt.Println("Cleaned Up") 47 | } 48 | -------------------------------------------------------------------------------- /5-select/example6/select.go: -------------------------------------------------------------------------------- 1 | /* 2 | Daisy-Chain 3 | 4 | 1 -> 2 3 4 5 6 7 5 | Gopher1 -> Gopher2 -> Gopher3 -> Gopher4 -> Gopher5 -> Gopher6 6 | */ 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | func main() { 14 | const n = 6 15 | rightmost := make(chan int) 16 | 17 | var left chan int 18 | right := rightmost 19 | 20 | // Launch Goroutines that each act as a gopher waiting for a message. 21 | for i := 0; i < n; i++ { 22 | left = make(chan int) 23 | go pass(left, right) // Gopher6 passes to Gopher7, Gopher5 passes to Gopher6, etc. 24 | right = left 25 | } 26 | 27 | fmt.Println("Goroutines Are Waiting") 28 | 29 | // Send the first message to Gopher1. 30 | go func(c chan int) { 31 | fmt.Println("Give Gopher1 the inital value") 32 | c <- 1 33 | }(left) 34 | 35 | // Wait for the message to reach the end. 36 | fmt.Printf("Final Value: %d\n", <-rightmost) 37 | } 38 | 39 | func pass(left, right chan int) { 40 | value := <-left 41 | right <- 1 + value 42 | 43 | fmt.Printf("Left[%d] Right[%d]\n", value, value+1) 44 | } 45 | -------------------------------------------------------------------------------- /6-search/example1/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example: Google Search 3 | 4 | Given a query, return a page of search results (and some ads). 5 | Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results. 6 | 7 | Google function takes a query and returns a slice of Results (which are just strings) 8 | Google invokes Web, Image and Video searches serially, appending them to the results slice. 9 | 10 | Run each search in series 11 | */ 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "math/rand" 17 | "time" 18 | ) 19 | 20 | var ( 21 | web = fakeSearch("web") 22 | image = fakeSearch("image") 23 | video = fakeSearch("video") 24 | ) 25 | 26 | type ( 27 | result string 28 | search func(query string) result 29 | ) 30 | 31 | func main() { 32 | rand.Seed(time.Now().UnixNano()) 33 | 34 | start := time.Now() 35 | results := google("golang") 36 | elapsed := time.Since(start) 37 | 38 | fmt.Println(results) 39 | fmt.Println(elapsed) 40 | } 41 | 42 | func fakeSearch(kind string) search { 43 | return func(query string) result { 44 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 45 | return result(fmt.Sprintf("%s result for %q\n", kind, query)) 46 | } 47 | } 48 | 49 | func google(query string) (results []result) { 50 | results = append(results, web(query)) 51 | results = append(results, image(query)) 52 | results = append(results, video(query)) 53 | 54 | return results 55 | } 56 | -------------------------------------------------------------------------------- /6-search/example2/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example: Google Search 3 | 4 | Given a query, return a page of search results (and some ads). 5 | Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results. 6 | 7 | Run the Web, Image and Video searches concurrently, and wait for all results. 8 | No locks. No condition variables. No callbacks 9 | 10 | Run each search in their own Goroutine and wait for all searches to complete before display results 11 | */ 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "math/rand" 17 | "time" 18 | ) 19 | 20 | var ( 21 | web = fakeSearch("web") 22 | image = fakeSearch("image") 23 | video = fakeSearch("video") 24 | ) 25 | 26 | type ( 27 | result string 28 | search func(query string) result 29 | ) 30 | 31 | func main() { 32 | rand.Seed(time.Now().UnixNano()) 33 | 34 | start := time.Now() 35 | results := google("golang") 36 | elapsed := time.Since(start) 37 | 38 | fmt.Println(results) 39 | fmt.Println(elapsed) 40 | } 41 | 42 | func fakeSearch(kind string) search { 43 | return func(query string) result { 44 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 45 | return result(fmt.Sprintf("%s result for %q\n", kind, query)) 46 | } 47 | } 48 | func google(query string) (results []result) { 49 | c := make(chan result) 50 | 51 | go func() { 52 | c <- web(query) 53 | }() 54 | 55 | go func() { 56 | c <- image(query) 57 | }() 58 | 59 | go func() { 60 | c <- video(query) 61 | }() 62 | 63 | for i := 0; i < 3; i++ { 64 | r := <-c 65 | results = append(results, r) 66 | } 67 | 68 | return results 69 | } 70 | -------------------------------------------------------------------------------- /6-search/example3/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example: Google Search 2.1 3 | 4 | Given a query, return a page of search results (and some ads). 5 | Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results. 6 | 7 | Don't wait for slow servers. No locks. No condition variables. No callbacks 8 | 9 | Run each search in their own Goroutine but only return any searches that complete in 10 | 80 Milliseconds or less 11 | */ 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "math/rand" 17 | "time" 18 | ) 19 | 20 | var ( 21 | web = fakeSearch("web") 22 | image = fakeSearch("image") 23 | video = fakeSearch("video") 24 | ) 25 | 26 | type ( 27 | result string 28 | search func(query string) result 29 | ) 30 | 31 | func main() { 32 | rand.Seed(time.Now().UnixNano()) 33 | 34 | start := time.Now() 35 | results := google("golang") 36 | elapsed := time.Since(start) 37 | 38 | fmt.Println(results) 39 | fmt.Println(elapsed) 40 | } 41 | 42 | func fakeSearch(kind string) search { 43 | return func(query string) result { 44 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 45 | return result(fmt.Sprintf("%s result for %q\n", kind, query)) 46 | } 47 | } 48 | func google(query string) (results []result) { 49 | c := make(chan result) 50 | 51 | go func() { 52 | c <- web(query) 53 | }() 54 | 55 | go func() { 56 | c <- image(query) 57 | }() 58 | 59 | go func() { 60 | c <- video(query) 61 | }() 62 | timeout := time.After(80 * time.Millisecond) 63 | 64 | for i := 0; i < 3; i++ { 65 | select { 66 | case r := <-c: 67 | results = append(results, r) 68 | case <-timeout: 69 | fmt.Println("timed out") 70 | return results 71 | } 72 | } 73 | 74 | return results 75 | } 76 | -------------------------------------------------------------------------------- /6-search/example4/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example: Google Search 2.1 - Avoid Timeouts 3 | 4 | Given a query, return a page of search results (and some ads). 5 | Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results. 6 | 7 | No locks. No condition variables. No callbacks 8 | 9 | Replicate the servers. Send requests to multiple replicas, and use the first response. 10 | 11 | Run the same search against multiple servers in their own Goroutine 12 | */ 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "math/rand" 18 | "time" 19 | ) 20 | 21 | type ( 22 | result string 23 | search func(query string) result 24 | ) 25 | 26 | func main() { 27 | rand.Seed(time.Now().UnixNano()) 28 | 29 | start := time.Now() 30 | 31 | // Run the search against two replicas 32 | result := first("golang", 33 | fakeSearch("replica 1"), 34 | fakeSearch("replica 2")) 35 | 36 | elapsed := time.Since(start) 37 | 38 | fmt.Println(result) 39 | fmt.Println(elapsed) 40 | } 41 | 42 | func fakeSearch(kind string) search { 43 | return func(query string) result { 44 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 45 | return result(fmt.Sprintf("%s result for %q\n", kind, query)) 46 | } 47 | } 48 | 49 | func first(query string, replicas ...search) result { 50 | c := make(chan result) 51 | 52 | // Define a function that takes the index to the replica function to use. 53 | // Then it executes that function writing the results to the channel. 54 | searchReplica := func(i int) { 55 | c <- replicas[i](query) 56 | } 57 | 58 | // Run each replica function in its own Goroutine. 59 | for i := range replicas { 60 | go searchReplica(i) 61 | } 62 | 63 | // As soon as one of the replica functions write a result, return. 64 | return <-c 65 | } 66 | -------------------------------------------------------------------------------- /6-search/example5/search.go: -------------------------------------------------------------------------------- 1 | /* 2 | Example: Google Search 3.0 3 | 4 | Given a query, return a page of search results (and some ads). 5 | Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results. 6 | 7 | No locks. No condition variables. No callbacks 8 | 9 | Reduce tail latency using replicated search servers 10 | 11 | Run the same search against multiple servers in their own Goroutine but only return searches 12 | that complete in 80 Milliseconds or less 13 | 14 | All three searches SHOULD always come back in under 80 milliseconds 15 | */ 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "math/rand" 21 | "time" 22 | ) 23 | 24 | var ( 25 | web1 = fakeSearch("web") 26 | web2 = fakeSearch("web") 27 | image1 = fakeSearch("image") 28 | image2 = fakeSearch("image") 29 | video1 = fakeSearch("video") 30 | video2 = fakeSearch("video") 31 | ) 32 | 33 | type ( 34 | result string 35 | search func(query string) result 36 | ) 37 | 38 | func main() { 39 | rand.Seed(time.Now().UnixNano()) 40 | 41 | start := time.Now() 42 | results := google("golang") 43 | elapsed := time.Since(start) 44 | 45 | fmt.Println(results) 46 | fmt.Println(elapsed) 47 | } 48 | 49 | func fakeSearch(kind string) search { 50 | return func(query string) result { 51 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 52 | return result(fmt.Sprintf("%s result for %q\n", kind, query)) 53 | } 54 | } 55 | 56 | func google(query string) (results []result) { 57 | c := make(chan result) 58 | 59 | go func() { 60 | c <- first(query, web1, web2) 61 | }() 62 | 63 | go func() { 64 | c <- first(query, image1, image2) 65 | }() 66 | 67 | go func() { 68 | c <- first(query, video1, video2) 69 | }() 70 | 71 | timeout := time.After(80 * time.Millisecond) 72 | 73 | for i := 0; i < 3; i++ { 74 | select { 75 | case r := <-c: 76 | results = append(results, r) 77 | case <-timeout: 78 | fmt.Println("timed out") 79 | return 80 | } 81 | } 82 | 83 | return results 84 | } 85 | 86 | func first(query string, replicas ...search) result { 87 | c := make(chan result) 88 | 89 | // Define a function that takes the index to the replica function to use. 90 | // Then it executes that function writing the results to the channel. 91 | searchReplica := func(i int) { 92 | c <- replicas[i](query) 93 | } 94 | 95 | // Run each replica function in its own Goroutine. 96 | for i := range replicas { 97 | go searchReplica(i) 98 | } 99 | 100 | // As soon as one of the replica functions write a result, return. 101 | return <-c 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Go Concurrency Patterns 2 | 3 | Examples taken from Rob Pike's talk about concurrency patterns. 4 | 5 | ### Go Concurrency Patterns video: 6 | 7 | https://www.youtube.com/watch?v=f6kdp27TYZs 8 | 9 | ### Notes from Rob's talk 10 | 11 | "The composition of independently executing computations" 12 | -- Rob Pike 13 | 14 | Concurrency not parallelism. 15 | On a single core, you can't have parallelism. 16 | 17 | #### Concurrency 18 | - Easy To Understand 19 | - Easy To Use 20 | - Easy To Reason 21 | - Work at a Higher Level 22 | 23 | #### Concurrency is not new 24 | Hoare's CSP Paper in 1978 25 | - Occam ('83), Erlang ('86), Newsqueak ('88), Concurrent ML ('93), Alef ('95), Limbo ('96) 26 | 27 | #### Go is a Branch of: 28 | Newsqueak-Alef-Limbo using Channels 29 | 30 | #### Goroutines 31 | - Independently executing function 32 | - Has its own stack which grows and shrinks 33 | - Very Cheap, could have thousands or more 34 | - Not a thread 35 | - Could have only one thread running thousands of goroutines 36 | - Goroutines are multiplexed dynamically onto threads as needed to keep routines running 37 | - Could think of it as a very cheap thread 38 | 39 | #### Buffered Channels 40 | - Channels can be created with a buffer 41 | - Buffering removes synchronization 42 | - Can be important for some problems but they are more subtle to reason about 43 | - Not using them today 44 | 45 | #### Summary 46 | - Started With 47 | - Slow, Sequential and Failure-Sensitive code 48 | - Ended With 49 | - Fast, Concurrent, Replicated and Robust code 50 | 51 | #### Other Patterns 52 | 53 | **Chatroulette Toy:** 54 | http://tinyurl.com/gochatroulette 55 | 56 | **Load Balancer:** 57 | http://tinyurl.com/goloadbalancer 58 | 59 | **Concurrent Prime Sieve:** 60 | http://tinyurl.com/gosieve 61 | 62 | **Concurrent Power Series (by Mcllroy)** 63 | http://tinyurl.com/gopowerseries 64 | --------------------------------------------------------------------------------