├── .gitignore ├── 04-delayed-computing ├── 02-lazy-eval │ └── main.go ├── 01-futures │ └── main.go └── 01.1-futures-with-retries │ └── main.go ├── 02-sync ├── 01-mutex │ └── main.go └── 02-semaphore │ └── main.go ├── 06-ring-buffer └── main.go ├── 03-parallel-computing ├── 02-queuing │ └── main.go ├── 03-parallel-for-loop │ └── main.go ├── 01-worker-pool │ └── main.go ├── 03.1-parallel-for-loop-with-bells-and-whistles │ └── main.go └── 04-parallel-for-loop-bounded │ └── main.go ├── 01-generative ├── 01-generator │ └── main.go ├── 04-fan-out │ └── main.go ├── 05-pipeline │ └── main.go ├── 02-fan-in │ └── main.go └── 03-fan-in-extended │ └── main.go └── 05-tee └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /04-delayed-computing/02-lazy-eval/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type LazyInt func() int 9 | 10 | func Make(f LazyInt) LazyInt { 11 | var v int 12 | var once sync.Once 13 | return func() int { 14 | once.Do(func() { 15 | v = f() 16 | f = nil // so that f can now be GC'ed 17 | }) 18 | return v 19 | } 20 | } 21 | 22 | func main() { 23 | n := Make(func() int { 24 | fmt.Println("Doing expensive calculations") 25 | return 23 26 | }) 27 | fmt.Println(n()) // Calculates the 23 28 | fmt.Println(n() + 42) // Reuses the calculated value 29 | } 30 | -------------------------------------------------------------------------------- /02-sync/01-mutex/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type mutexType chan struct{} 9 | type mutex struct { 10 | s mutexType 11 | } 12 | 13 | func NewMutex() mutex { 14 | return mutex{s: make(mutexType, 1)} 15 | } 16 | 17 | func (m mutex) Lock() { 18 | e := struct{}{} 19 | m.s <- e 20 | } 21 | 22 | func (m mutex) Unlock() { 23 | <-m.s 24 | } 25 | 26 | const N = 1000 27 | 28 | func main() { 29 | m := NewMutex() 30 | counter := 0 31 | wg := sync.WaitGroup{} 32 | 33 | wg.Add(N) 34 | for i := 1; i <= N; i++ { 35 | go func() { 36 | m.Lock() 37 | counter++ 38 | m.Unlock() 39 | wg.Done() 40 | }() 41 | } 42 | 43 | wg.Wait() 44 | fmt.Printf("Mutex counter: %d\n", counter) 45 | } 46 | -------------------------------------------------------------------------------- /06-ring-buffer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func NewRingBuffer(inCh, outCh chan int) *ringBuffer { 8 | return &ringBuffer{ 9 | inCh: inCh, 10 | outCh: outCh, 11 | } 12 | } 13 | 14 | type ringBuffer struct { 15 | inCh chan int 16 | outCh chan int 17 | } 18 | 19 | func (r *ringBuffer) Run() { 20 | defer close(r.outCh) 21 | for v := range r.inCh { 22 | select { 23 | case r.outCh <- v: 24 | default: 25 | <-r.outCh 26 | r.outCh <- v 27 | } 28 | } 29 | } 30 | 31 | func main() { 32 | inCh := make(chan int) 33 | outCh := make(chan int, 4) 34 | rb := NewRingBuffer(inCh, outCh) 35 | go rb.Run() 36 | 37 | for i := 1; i <= 10; i++ { 38 | inCh <- i 39 | } 40 | 41 | close(inCh) 42 | 43 | for res := range outCh { 44 | fmt.Println(res) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /03-parallel-computing/02-queuing/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const N = 3 10 | const MESSAGES = 10 11 | 12 | func main() { 13 | var wg sync.WaitGroup 14 | 15 | fmt.Println("Queue of length N:", N) 16 | queue := make(chan struct{}, N) 17 | 18 | wg.Add(MESSAGES) 19 | 20 | for w := 1; w <= MESSAGES; w++ { 21 | process(w, queue, &wg) 22 | } 23 | 24 | wg.Wait() 25 | 26 | close(queue) 27 | fmt.Println("Processing completed") 28 | } 29 | 30 | func process(payload int, queue chan struct{}, wg *sync.WaitGroup) { 31 | queue <- struct{}{} 32 | 33 | go func() { 34 | defer wg.Done() 35 | 36 | fmt.Printf("Start processing of %d\n", payload) 37 | time.Sleep(time.Millisecond * 500) 38 | fmt.Printf("Completed processing of %d\n", payload) 39 | 40 | <-queue 41 | }() 42 | } 43 | -------------------------------------------------------------------------------- /04-delayed-computing/01-futures/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type data struct { 9 | Body string 10 | Error error 11 | } 12 | 13 | func doGet(url string) (string, error) { 14 | time.Sleep(time.Millisecond * 200) 15 | return fmt.Sprintf("Response of %s", url), nil 16 | } 17 | 18 | func future(url string) <-chan data { 19 | c := make(chan data, 1) 20 | 21 | go func() { 22 | body, err := doGet(url) 23 | 24 | c <- data{Body: body, Error: err} 25 | }() 26 | 27 | return c 28 | } 29 | 30 | func main() { 31 | future1 := future("https://example1.com") 32 | future2 := future("https://example2.com") 33 | 34 | fmt.Println("Requests started") 35 | 36 | response1 := <-future1 37 | response2 := <-future2 38 | 39 | fmt.Printf("Response 1: %v\n", response1) 40 | fmt.Printf("Response 2: %v\n", response2) 41 | } 42 | -------------------------------------------------------------------------------- /01-generative/01-generator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | done := make(chan struct{}) 11 | wg := sync.WaitGroup{} 12 | wg.Add(2) 13 | 14 | ch := makeGenerator(done, &wg) 15 | 16 | go func() { 17 | defer wg.Done() 18 | for v := range ch { 19 | fmt.Println("value:", v) 20 | } 21 | }() 22 | 23 | time.Sleep(time.Second) 24 | close(done) 25 | wg.Wait() 26 | } 27 | 28 | func makeGenerator(done <-chan struct{}, wg *sync.WaitGroup) <-chan int { 29 | ch := make(chan int, 1) 30 | var i = 0 31 | 32 | go func() { 33 | defer wg.Done() 34 | for { 35 | select { 36 | case <-done: 37 | close(ch) 38 | fmt.Println("done") 39 | return 40 | default: 41 | time.Sleep(time.Millisecond * 250) 42 | ch <- i 43 | i++ 44 | } 45 | } 46 | }() 47 | 48 | return ch 49 | } 50 | -------------------------------------------------------------------------------- /01-generative/04-fan-out/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | wg := sync.WaitGroup{} 10 | work := []int{1, 2, 3, 4, 5, 6, 7, 8} 11 | 12 | wg.Add(1) 13 | in := generateWork(work, &wg) 14 | 15 | wg.Add(3) 16 | fanOut(in, "Alice", &wg) 17 | fanOut(in, "Jack", &wg) 18 | fanOut(in, "Bob", &wg) 19 | 20 | wg.Wait() 21 | } 22 | 23 | func fanOut(in <-chan int, name string, wg *sync.WaitGroup) { 24 | go func() { 25 | defer wg.Done() 26 | 27 | for data := range in { 28 | fmt.Println(name, "processed", data) 29 | } 30 | }() 31 | } 32 | 33 | func generateWork(work []int, wg *sync.WaitGroup) <-chan int { 34 | ch := make(chan int) 35 | 36 | go func() { 37 | defer wg.Done() 38 | defer close(ch) 39 | 40 | for _, w := range work { 41 | ch <- w 42 | } 43 | fmt.Println("All data written") 44 | }() 45 | 46 | return ch 47 | } 48 | -------------------------------------------------------------------------------- /03-parallel-computing/03-parallel-for-loop/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type empty struct{} 9 | 10 | const DATA_SIZE = 4 11 | 12 | func calculate(val int) int { 13 | time.Sleep(time.Millisecond * 500) 14 | return val * 2 15 | } 16 | 17 | func main() { 18 | data := make([]int, 0, DATA_SIZE) 19 | for i := 0; i < DATA_SIZE; i++ { 20 | data = append(data, i+10) 21 | } 22 | results := make([]int, DATA_SIZE) 23 | semaphore := make(chan empty, DATA_SIZE) 24 | 25 | fmt.Printf("Before: %v\n", data) 26 | start := time.Now() 27 | 28 | for i, xi := range data { 29 | go func(i int, xi int) { 30 | results[i] = calculate(xi) 31 | semaphore <- empty{} 32 | }(i, xi) 33 | } 34 | for i := 0; i < DATA_SIZE; i++ { 35 | <-semaphore 36 | } 37 | 38 | fmt.Printf(" After: %v\n", results) 39 | fmt.Printf(" Elapsed: %s\n", time.Since(start)) 40 | } 41 | -------------------------------------------------------------------------------- /02-sync/02-semaphore/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type semaphore chan struct{} 9 | 10 | func NewSemaphore(n int) semaphore { 11 | return make(semaphore, n) 12 | } 13 | 14 | func (s semaphore) Acquire(n int) { 15 | e := struct{}{} 16 | for i := 0; i < n; i++ { 17 | s <- e 18 | } 19 | } 20 | 21 | func (s semaphore) Release(n int) { 22 | for i := 0; i < n; i++ { 23 | <-s 24 | } 25 | } 26 | 27 | const N = 3 28 | const TOTAL = 10 29 | 30 | func main() { 31 | sem := NewSemaphore(N) 32 | done := make(chan bool) 33 | for i := 1; i <= TOTAL; i++ { 34 | sem.Acquire(1) 35 | go func(v int) { 36 | defer sem.Release(1) 37 | process(v) 38 | if v == TOTAL { 39 | done <- true 40 | } 41 | }(i) 42 | } 43 | <-done 44 | } 45 | func process(id int) { 46 | fmt.Printf("[%s]: running task %d\n", time.Now().Format("15:04:05"), id) 47 | time.Sleep(time.Second) 48 | } 49 | -------------------------------------------------------------------------------- /03-parallel-computing/01-worker-pool/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const totalJobs = 10 8 | const totalWorkers = 2 9 | 10 | func main() { 11 | jobs := make(chan int, totalJobs) 12 | results := make(chan int, totalJobs) 13 | 14 | for w := 1; w <= totalWorkers; w++ { 15 | go worker(w, jobs, results) 16 | } 17 | 18 | // Send jobs 19 | for j := 1; j <= totalJobs; j++ { 20 | jobs <- j 21 | } 22 | 23 | close(jobs) 24 | 25 | // Receive results 26 | for a := 1; a <= totalJobs; a++ { 27 | <-results 28 | } 29 | 30 | close(results) 31 | } 32 | 33 | func worker(id int, jobs <-chan int, results chan<- int) { 34 | for j := range jobs { 35 | go func(job int) { 36 | fmt.Printf("Worker %d started job %d\n", id, job) 37 | 38 | // Do work and send result 39 | result := job * 2 40 | results <- result 41 | 42 | fmt.Printf("Worker %d finished job %d\n", id, job) 43 | }(j) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /03-parallel-computing/03.1-parallel-for-loop-with-bells-and-whistles/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | const DATA_SIZE = 4 10 | 11 | func calculate(val int) int { 12 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(500))) 13 | return val * 2 14 | } 15 | 16 | func main() { 17 | data := make([]int, 0, DATA_SIZE) 18 | for i := 0; i < DATA_SIZE; i++ { 19 | data = append(data, i+10) 20 | } 21 | results := make([]int, DATA_SIZE) 22 | semaphore := make(chan int, DATA_SIZE) 23 | 24 | fmt.Printf("Before: %v\n", data) 25 | start := time.Now() 26 | 27 | for i, xi := range data { 28 | go func(i int, xi int) { 29 | results[i] = calculate(xi) 30 | semaphore <- results[i] 31 | }(i, xi) 32 | } 33 | for i := 0; i < DATA_SIZE; i++ { 34 | fmt.Printf(" one calculation completed: %d\n", <-semaphore) 35 | } 36 | 37 | fmt.Printf(" After: %v\n", results) 38 | fmt.Printf(" Elapsed: %s\n", time.Since(start)) 39 | } 40 | -------------------------------------------------------------------------------- /03-parallel-computing/04-parallel-for-loop-bounded/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type empty struct{} 10 | 11 | const DATA_SIZE = 10 12 | const SEMAPHORE_LIMIT = 3 13 | 14 | func calculate(val int) int { 15 | fmt.Printf("[%s] Calc for %d\n", time.Now().Format("15:04:05"), val) 16 | time.Sleep(time.Millisecond * 1200) 17 | return val * 2 18 | } 19 | 20 | func main() { 21 | data := make([]int, 0, DATA_SIZE) 22 | for i := 0; i < DATA_SIZE; i++ { 23 | data = append(data, i+1) 24 | } 25 | results := make([]int, DATA_SIZE) 26 | semaphore := make(chan empty, SEMAPHORE_LIMIT) 27 | wg := sync.WaitGroup{} 28 | 29 | fmt.Printf("Before: %v\n", data) 30 | start := time.Now() 31 | 32 | for i, xi := range data { 33 | wg.Add(1) 34 | go func(i int, xi int) { 35 | defer wg.Done() 36 | semaphore <- empty{} 37 | results[i] = calculate(xi) 38 | <-semaphore 39 | }(i, xi) 40 | } 41 | 42 | wg.Wait() 43 | fmt.Printf(" After: %v\n", results) 44 | fmt.Printf(" Elapsed: %s\n", time.Since(start)) 45 | } 46 | -------------------------------------------------------------------------------- /05-tee/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func tee(ctx context.Context, input <-chan string, outputs []chan<- string) { 10 | for elem := range input { 11 | elem := elem 12 | for _, out := range outputs { 13 | out := out 14 | go func() { 15 | select { 16 | case out <- elem: 17 | break 18 | case <-ctx.Done(): 19 | break 20 | } 21 | }() 22 | } 23 | } 24 | } 25 | 26 | func main() { 27 | in := make(chan string) 28 | out1 := make(chan string) 29 | out2 := make(chan string) 30 | ctx, cancel := context.WithCancel(context.Background()) 31 | outputs := []chan<- string{ 32 | out1, out2, 33 | } 34 | 35 | go func() { 36 | in <- "A" 37 | in <- "B" 38 | in <- "C" 39 | close(in) 40 | }() 41 | 42 | tee(ctx, in, outputs) 43 | go func() { 44 | for { 45 | select { 46 | case <-ctx.Done(): 47 | break 48 | case v := <-out1: 49 | fmt.Printf("out1 got value: %s\n", v) 50 | case v := <-out2: 51 | fmt.Printf("out2 got value: %s\n", v) 52 | } 53 | } 54 | }() 55 | 56 | time.Sleep(time.Second) 57 | cancel() 58 | } 59 | -------------------------------------------------------------------------------- /04-delayed-computing/01.1-futures-with-retries/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | type data struct { 11 | Body string 12 | Error error 13 | } 14 | 15 | func doGet(url string) (string, error) { 16 | time.Sleep(time.Millisecond * 200) 17 | failure := rand.Int()%10 > 5 18 | if failure { 19 | return "", errors.New("timeout") 20 | } 21 | return fmt.Sprintf("Response of %s", url), nil 22 | } 23 | 24 | func future(url string) <-chan data { 25 | c := make(chan data, 1) 26 | 27 | go func() { 28 | for i := 1; i <= 3; i++ { 29 | body, err := doGet(url) 30 | if err != nil && i != 3 { 31 | fmt.Println("got error", err, "retrying") 32 | time.Sleep(time.Millisecond * 10) 33 | continue 34 | } 35 | 36 | c <- data{Body: body, Error: err} 37 | } 38 | }() 39 | 40 | return c 41 | } 42 | 43 | func main() { 44 | rand.Seed(time.Now().Unix()) 45 | future1 := future("https://example1.com") 46 | future2 := future("https://example2.com") 47 | 48 | fmt.Println("Requests started") 49 | 50 | response1 := <-future1 51 | response2 := <-future2 52 | 53 | fmt.Printf("Response 1: %v\n", response1) 54 | fmt.Printf("Response 2: %v\n", response2) 55 | } 56 | -------------------------------------------------------------------------------- /01-generative/05-pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func main() { 9 | in := generateWork([]int{0, 1, 2, 3, 4, 5, 6, 7, 8}) 10 | 11 | out := filterOdd(in) // оставляем только четные числа 12 | out = square(out) // возводим в квадрат 13 | out = half(out) // и делим пополам 14 | 15 | for value := range out { 16 | fmt.Println(value) 17 | } 18 | } 19 | 20 | func filterOdd(in <-chan int) <-chan int { 21 | out := make(chan int) 22 | 23 | go func() { 24 | defer close(out) 25 | 26 | for i := range in { 27 | if i%2 == 0 { 28 | out <- i 29 | } 30 | } 31 | }() 32 | 33 | return out 34 | } 35 | 36 | func square(in <-chan int) <-chan int { 37 | out := make(chan int) 38 | 39 | go func() { 40 | defer close(out) 41 | 42 | for i := range in { 43 | value := math.Pow(float64(i), 2) 44 | out <- int(value) 45 | } 46 | }() 47 | 48 | return out 49 | } 50 | 51 | func half(in <-chan int) <-chan int { 52 | out := make(chan int) 53 | 54 | go func() { 55 | defer close(out) 56 | 57 | for i := range in { 58 | value := i / 2 59 | out <- value 60 | } 61 | }() 62 | 63 | return out 64 | } 65 | 66 | func generateWork(work []int) <-chan int { 67 | ch := make(chan int) 68 | 69 | go func() { 70 | defer close(ch) 71 | 72 | for _, w := range work { 73 | ch <- w 74 | } 75 | }() 76 | 77 | return ch 78 | } 79 | -------------------------------------------------------------------------------- /01-generative/02-fan-in/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type payload struct { 10 | name string 11 | value int 12 | } 13 | 14 | func producer(name string, done <-chan struct{}, wg *sync.WaitGroup) <-chan payload { 15 | ch := make(chan payload) 16 | var i = 1 17 | go func() { 18 | defer wg.Done() 19 | for { 20 | select { 21 | case <-done: 22 | close(ch) 23 | fmt.Println(name, "completed") 24 | return 25 | case ch <- payload{ 26 | name: name, 27 | value: i, 28 | }: 29 | fmt.Println(name, "produced", i) 30 | i++ 31 | time.Sleep(time.Millisecond * 500) 32 | } 33 | } 34 | }() 35 | return ch 36 | } 37 | 38 | func consumer(channels []<-chan payload, done <-chan struct{}, wg *sync.WaitGroup, fanIn chan<- payload) { 39 | for i, ch := range channels { 40 | i := i + 1 41 | ch := ch 42 | go func() { 43 | defer wg.Done() 44 | fmt.Println("started consume of producer", i) 45 | for { 46 | select { 47 | case <-done: 48 | fmt.Println("consume of producer", i, "completed") 49 | return 50 | case v := <-ch: 51 | fmt.Println("consumer of producer", i, "got value", v.value, "from", v.name) 52 | fanIn <- v 53 | } 54 | } 55 | }() 56 | } 57 | } 58 | 59 | func main() { 60 | done := make(chan struct{}) 61 | wg := sync.WaitGroup{} 62 | 63 | wg.Add(2) 64 | producers := make([]<-chan payload, 0, 3) 65 | producers = append(producers, producer("Alice", done, &wg)) 66 | producers = append(producers, producer("Jack", done, &wg)) 67 | 68 | fanIn := make(chan payload, 0) 69 | 70 | wg.Add(2) 71 | consumer(producers, done, &wg, fanIn) 72 | 73 | go func() { 74 | for { 75 | fanInDone := false 76 | select { 77 | case <-done: 78 | if !fanInDone { 79 | continue 80 | } 81 | return 82 | case v, ok := <-fanIn: 83 | if !ok { 84 | fanInDone = true 85 | } 86 | fmt.Printf("fanIn got %v\n", v) 87 | } 88 | } 89 | }() 90 | 91 | time.Sleep(time.Second) 92 | close(done) 93 | wg.Wait() 94 | } 95 | -------------------------------------------------------------------------------- /01-generative/03-fan-in-extended/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type payload struct { 10 | name string 11 | value int 12 | } 13 | 14 | func producer(name string, done <-chan struct{}, wg *sync.WaitGroup) <-chan payload { 15 | ch := make(chan payload) 16 | var i = 1 17 | go func() { 18 | defer wg.Done() 19 | for { 20 | select { 21 | case <-done: 22 | close(ch) 23 | fmt.Println(name, "completed") 24 | return 25 | case ch <- payload{ 26 | name: name, 27 | value: i, 28 | }: 29 | fmt.Println(name, "produced", i) 30 | i++ 31 | time.Sleep(time.Millisecond * 500) 32 | } 33 | } 34 | }() 35 | return ch 36 | } 37 | 38 | func consumer(name string, channels []<-chan payload, done <-chan struct{}, wg *sync.WaitGroup, fanIn chan<- payload) { 39 | for i, ch := range channels { 40 | i := i + 1 41 | ch := ch 42 | go func() { 43 | defer wg.Done() 44 | fmt.Println("started consumer", name, i) 45 | for { 46 | select { 47 | case <-done: 48 | fmt.Println("consumer", name, i, "completed") 49 | return 50 | case v := <-ch: 51 | fmt.Println("Consumer", name, i, "got value", v.value, "from", v.name) 52 | fanIn <- v 53 | } 54 | } 55 | }() 56 | } 57 | } 58 | 59 | func main() { 60 | done := make(chan struct{}) 61 | wg := sync.WaitGroup{} 62 | 63 | wg.Add(3) 64 | producers := make([]<-chan payload, 0, 3) 65 | producers = append(producers, producer("Alice", done, &wg)) 66 | producers = append(producers, producer("Jack", done, &wg)) 67 | producers = append(producers, producer("Bob", done, &wg)) 68 | 69 | fanIn1 := make(chan payload, 0) 70 | fanIn2 := make(chan payload, 0) 71 | 72 | wg.Add(3) 73 | consumer("C1", producers, done, &wg, fanIn1) 74 | 75 | wg.Add(3) 76 | consumer("C2", producers, done, &wg, fanIn2) 77 | 78 | go func() { 79 | f1Done := false 80 | f2Done := false 81 | for { 82 | select { 83 | case <-done: 84 | if f1Done && f2Done { 85 | return 86 | } 87 | case v, ok := <-fanIn1: 88 | if !ok { 89 | f1Done = true 90 | continue 91 | } 92 | fmt.Printf("fanIn1 got %v\n", v) 93 | case v, ok := <-fanIn2: 94 | if !ok { 95 | f2Done = true 96 | continue 97 | } 98 | fmt.Printf("fanIn2 got %v\n", v) 99 | } 100 | } 101 | }() 102 | 103 | time.Sleep(time.Second) 104 | close(done) 105 | wg.Wait() 106 | } 107 | --------------------------------------------------------------------------------