├── .gitignore ├── README.md ├── daisy-chain └── prime-sieve.go ├── generator ├── auto_increment_id.go └── rand01.go ├── multiplexing └── sample-calc.go ├── select ├── example.go ├── quit_chan_case.go └── timeout_case.go ├── service └── nofity_service.go ├── small-problems └── palindrome.go └── timer └── timer.go /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .*.swo 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go-patterns-with-channel 2 | 3 | Go语言信道和goroutine的一些设计模式的简单例子。 4 | 5 | 这些设计模式和应用场景的介绍在 [writings.sh/post/goroutine-guide-part-2](https://writings.sh/post/goroutine-guide-part-2) 6 | -------------------------------------------------------------------------------- /daisy-chain/prime-sieve.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 利用信道菊花链筛法求某一个整数范围的素数 3 | * 筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, 4 | * 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。 5 | * 依次类推,直到筛子为空时结束 6 | */ 7 | package main 8 | 9 | import "fmt" 10 | 11 | func xrange() chan int{ // 从2开始自增的整数生成器 12 | var ch chan int = make(chan int) 13 | 14 | go func() { // 开出一个goroutine 15 | for i := 2; ; i++ { 16 | ch <- i // 直到信道索要数据,才把i添加进信道 17 | } 18 | }() 19 | 20 | return ch 21 | } 22 | 23 | 24 | func filter(in chan int, number int) chan int { 25 | // 输入一个整数队列,筛出是number倍数的, 不是number的倍数的放入输出队列 26 | // in: 输入队列 27 | out := make(chan int) 28 | 29 | go func() { 30 | for { 31 | i := <- in // 从输入中取一个 32 | 33 | if i % number != 0 { 34 | out <- i // 放入输出信道 35 | } 36 | } 37 | }() 38 | 39 | return out 40 | } 41 | 42 | 43 | func main() { 44 | const max = 100 // 找出100以内的所有素数 45 | nums := xrange() // 初始化一个整数生成器 46 | number := <-nums // 从生成器中抓一个整数(2), 作为初始化整数 47 | 48 | for number <= max { // number作为筛子,当筛子超过max的时候结束筛选 49 | fmt.Println(number) // 打印素数, 筛子即一个素数 50 | nums = filter(nums, number) //筛掉number的倍数 51 | number = <- nums // 更新筛子 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /generator/auto_increment_id.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 一个使用信道生成自增整数的生成器的例子 3 | */ 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | 10 | func xrange() chan int{ // xrange用来生成自增的整数 11 | var ch chan int = make(chan int) 12 | 13 | go func() { // 开出一个goroutine 14 | for i := 0; ; i++ { 15 | ch <- i // 直到信道索要数据,才把i添加进信道 16 | } 17 | }() 18 | 19 | return ch 20 | } 21 | 22 | func main() { 23 | 24 | generator := xrange() 25 | 26 | for i:=0; i < 1000; i++ { // 我们生成1000个自增的整数! 27 | fmt.Println(<-generator) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /generator/rand01.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 利用select随机选择的性质,制作一个随机01生成器 3 | * 无缓冲信道每次只允许一个数据流通,select会随机选择一个进入。 4 | */ 5 | 6 | package main 7 | 8 | import "fmt" 9 | 10 | func rand01() chan int { 11 | ch := make(chan int) 12 | 13 | go func () { 14 | for { 15 | select { //select会尝试执行各个case, 如果都可以执行,那么随机选一个执行 16 | case ch <- 0: 17 | case ch <- 1: 18 | } 19 | } 20 | }() 21 | 22 | return ch 23 | } 24 | 25 | 26 | func main() { 27 | generator := rand01() //初始化一个01随机生成器 28 | 29 | //测试,打印10个随机01 30 | for i := 0; i < 10; i++ { 31 | fmt.Println(<-generator) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /multiplexing/sample-calc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 一个多路信道复合流入统一的输出信道的例子 3 | * 该例为模拟计算一个比较费时的事情,最后统一输出 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "time" 11 | "math/rand" 12 | ) 13 | 14 | func do_stuff(x int) int { // 一个比较耗时的事情,比如计算 15 | time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) //模拟计算 16 | return 100 - x // 假如100-x是一个很费时的计算 17 | } 18 | 19 | func branch(x int) chan int{ // 每个分支开出一个goroutine做计算并把计算结果流入各自信道 20 | ch := make(chan int) 21 | go func() { 22 | ch <- do_stuff(x) 23 | }() 24 | return ch 25 | } 26 | 27 | func fanIn(chs... chan int) chan int { 28 | ch := make(chan int) 29 | 30 | for _, c := range chs { 31 | go func(c chan int) {ch <- <- c}(c) // 注意此处明确传值 32 | } 33 | 34 | return ch 35 | } 36 | 37 | 38 | func main() { 39 | result := fanIn(branch(1), branch(2), branch(3)) 40 | 41 | for i := 0; i < 3; i++ { 42 | fmt.Println(<-result) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /select/example.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 一个select的使用例子 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | ) 10 | 11 | 12 | func foo(i int) chan int { 13 | c := make(chan int) 14 | go func () { c <- i }() 15 | return c 16 | } 17 | 18 | 19 | func main() { 20 | c1, c2, c3 := foo(1), foo(2), foo(3) 21 | 22 | c := make(chan int) 23 | 24 | go func() { // 开一个goroutine监视各个信道数据输出并收集数据到信道c 25 | for { 26 | select { // 监视c1, c2, c3的流出,并全部流入信道c 27 | case v1 := <- c1: c <- v1 28 | case v2 := <- c2: c <- v2 29 | case v3 := <- c3: c <- v3 30 | } 31 | } 32 | }() 33 | 34 | // 阻塞主线,取出信道c的数据 35 | for i := 0; i < 3; i++ { 36 | fmt.Println(<-c) // 从打印来看我们的数据输出并不是严格的1,2,3顺序 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /select/quit_chan_case.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 一个select监听信道数据流动并对退出信号作处理的例子 3 | */ 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | 11 | func main() { 12 | 13 | c, quit := make(chan int), make(chan int) 14 | 15 | go func() { 16 | c <- 2 // 添加数据 17 | quit <- 1 // 发送完成信号 18 | } () 19 | 20 | for is_quit := false; !is_quit; { 21 | select { // 监视信道c的数据流出 22 | case v := <-c: fmt.Printf("received %d from c", v) 23 | case <-quit: is_quit = true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /select/timeout_case.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 一个对select监听信道作超时处理的例子 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | 13 | func foo(i int) chan int { 14 | c := make(chan int) 15 | go func () { c <- i }() 16 | return c 17 | } 18 | 19 | 20 | func main() { 21 | c1, c2, c3 := foo(1), foo(2), foo(3) 22 | 23 | timeout := time.After(1 * time.Second) // timeout 是一个计时信道, 如果达到时间了,就会发一个信号出来 24 | 25 | for is_timeout := false; !is_timeout; { 26 | select { // 监视信道c1, c2, c3, timeout信道的数据流出 27 | case v1 := <- c1: fmt.Printf("received %d from c1", v1) 28 | case v2 := <- c2: fmt.Printf("received %d from c2", v2) 29 | case v3 := <- c3: fmt.Printf("received %d from c3", v3) 30 | case <- timeout: is_timeout = true // 超时 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /service/nofity_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | * goroutine的应用场景之一: 挂为一个独立的服务。 3 | * 该例为单独开一个查询消息通知的服务 4 | */ 5 | 6 | package main 7 | 8 | import "fmt" 9 | 10 | func get_notification(user string) chan string{ 11 | /* 12 | * 此处可以查询数据库获取新消息等等.. 13 | */ 14 | notifications := make(chan string) 15 | 16 | go func() { // 悬挂一个信道出去 17 | notifications <- fmt.Sprintf("Hi %s, welcome to weibo.com!", user) 18 | }() 19 | 20 | return notifications 21 | } 22 | 23 | func main() { 24 | jack := get_notification("jack") // 获取jack的消息 25 | joe := get_notification("joe") // 获取joe的消息 26 | 27 | // 获取消息的返回 28 | fmt.Println(<-jack) 29 | fmt.Println(<-joe) 30 | } 31 | -------------------------------------------------------------------------------- /small-problems/palindrome.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 用信道作回文字符串判断 3 | */ 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | 10 | func is_palindrome (str string) bool { // 判断是否是回文数 11 | 12 | ch := make(chan byte) 13 | 14 | length := len(str) 15 | 16 | go func() { 17 | for i := 0; i < length; i++ { 18 | ch <- str[i] 19 | } 20 | }() 21 | 22 | for i := length-1; i >=0 ; i-- { 23 | if <- ch != str[i] { 24 | return false 25 | } 26 | } 27 | 28 | return true 29 | } 30 | 31 | 32 | func main() { 33 | fmt.Println(is_palindrome("hello")) // false 34 | fmt.Println(is_palindrome("hellooll")) // false 35 | fmt.Println(is_palindrome("helloolleh")) // true 36 | } 37 | -------------------------------------------------------------------------------- /timer/timer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 利用信道做定时器 3 | */ 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | 13 | func timer(duration time.Duration) chan bool { 14 | ch := make(chan bool) 15 | 16 | go func() { 17 | time.Sleep(duration) 18 | ch <- true // 到时间啦! 19 | }() 20 | 21 | return ch 22 | } 23 | 24 | func main() { 25 | timeout := timer(time.Second) // 定时1s 26 | 27 | for { 28 | select { 29 | case <- timeout: 30 | fmt.Println("already 1s!") // 到时间 31 | return //结束程序 32 | } 33 | } 34 | } 35 | --------------------------------------------------------------------------------