├── .gitignore ├── LICENSE ├── README.md ├── eapache └── main.go ├── fanin └── main.go ├── fanout └── main.go ├── fanout2 └── main.go ├── flat └── main.go ├── mapreduce └── main.go ├── or_channel └── main.go ├── or_channel_go └── main.go ├── or_channel_rec └── main.go ├── or_channel_reflect └── main.go ├── or_done_channel └── main.go ├── perf └── chan_test.go ├── pipeline └── main.go ├── stream └── main.go ├── trylock └── main.go ├── trylock_channel └── main.go ├── trylock_channel2 └── main.go └── trylock_timeout └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 smallnest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Channel 应用模式 2 | 3 | 4 | 5 | ## Lock/TryLock模式 6 | 7 | ### 最高效的TryLock 8 | 9 | [trylock]() 10 | 11 | ### 使用Channel实现TryLock 12 | 13 | [trylock_channel]() 14 | 15 | ### 使用Channel实现Timeout功能的TryLock 16 | 17 | [trylock_timeout]() 18 | 19 | ## or 信号模式 20 | 从多个channel读取一个信号, 一旦读取到一个信号,则不再读取。 21 | 22 | 比如向多个服务器发送相同的http request,每个请求的结果放在单独的一个channel中, 只要其中一个服务器返回结果,则其它请求就被忽略。 23 | 24 | ### or channel by goroutine 25 | 26 | [or_channel_go]() 27 | 28 | 最简单的方式就是为每个channel启动一个goroutine, 每个goroutine读取自己负责的channel,一旦读取到一个信号,就关闭返回的channel。 29 | 显然,为每个channel启动一个goroutine太浪费了,虽然goroutine是一种轻量级的实现,但是如果数量巨大的情况下也会导致资源的大量占用以及调度上的性能低下。 30 | 31 | 32 | ### or channel (递归) 33 | 34 | [or_channel]() 35 | 基于递归的方式实现, 使用依次递归的方式 36 | 37 | ### or channel (递归) 38 | 39 | [or_channel_rec]() 40 | 基于递归的方式实现, 使用分而治之的方式 41 | 42 | ### or channel reflect 43 | 44 | [or_channel_rec]() 45 | 基于反射的方式 46 | 47 | ## or_done_channel模式 48 | 49 | 与上面的or 信号模式不同, `or done channel`模式是从一个channel中读取数据,只有当channel被关闭,或者done 信号channel被关闭的时候,读取操作才退出。 50 | 51 | [or_done_channel]() 52 | 53 | 如果将`done`这个信号channel换成 `context`,则可以依靠 `context.WithCancel` 来cancel读取,和这个模式类似。 54 | 55 | ## flat 模式 56 | 57 | [flat]() 58 | 将多个channels平展成一个channels。 与`Fan In`不同的是,输入的channels是从一个channel中读取出来的,而`Fan In`模式中的channels是一个channel slice。 59 | 60 | ## map/reduce 模式 61 | 62 | [mapreduce]() 63 | 64 | 65 | ## Fan In 扇入模式 66 | 67 | 将多个channel合并成一个channel 68 | 69 | [fanIn]() 70 | 71 | ## Fan Out 扇出模式 72 | 73 | 将一个Channel分成多个Channel。 有两种情况, 一种是每个channel都包含同样的数据(复制模式), 另一种将原数据均匀分布到各输出channel中(分布模式) 74 | 75 | - 复制模式 76 | - [fanOut]() 77 | - [fanOutReflect]() 78 | - 分布模式 79 | - [fanOut]() 80 | - [fanOutReflect]() 81 | 82 | ## References 83 | 1. https://github.com/kat-co/concurrency-in-go-src 84 | 2. https://github.com/campoy/justforfunc/tree/master/27-merging-chans 85 | 3. https://github.com/eapache/channels 86 | 4. https://github.com/LK4D4/trylock 87 | 5. https://github.com/lrita/gosync -------------------------------------------------------------------------------- /eapache/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/eapache/channels" 7 | ) 8 | 9 | func testPipe() { 10 | fmt.Println("pipe:") 11 | a := channels.NewNativeChannel(channels.None) 12 | b := channels.NewNativeChannel(channels.None) 13 | 14 | channels.Pipe(a, b) 15 | // channels.WeakPipe(a, b) 16 | 17 | go func() { 18 | for i := 0; i < 5; i++ { 19 | a.In() <- i 20 | } 21 | a.Close() 22 | }() 23 | 24 | for v := range b.Out() { 25 | fmt.Printf("%d ", v) 26 | } 27 | } 28 | 29 | func testDist() { 30 | fmt.Println("dist:") 31 | a := channels.NewNativeChannel(channels.None) 32 | outputs := []channels.Channel{ 33 | channels.NewNativeChannel(channels.None), 34 | channels.NewNativeChannel(channels.None), 35 | channels.NewNativeChannel(channels.None), 36 | channels.NewNativeChannel(channels.None), 37 | } 38 | 39 | channels.Distribute(a, outputs[0], outputs[1], outputs[2], outputs[3]) 40 | //channels.WeakDistribute(a, outputs[0], outputs[1], outputs[2], outputs[3]) 41 | 42 | go func() { 43 | for i := 0; i < 5; i++ { 44 | a.In() <- i 45 | } 46 | a.Close() 47 | }() 48 | 49 | for i := 0; i < 6; i++ { 50 | var v interface{} 51 | var j int 52 | select { 53 | case v = <-outputs[0].Out(): 54 | j = 0 55 | case v = <-outputs[1].Out(): 56 | j = 1 57 | case v = <-outputs[2].Out(): 58 | j = 2 59 | case v = <-outputs[3].Out(): 60 | j = 3 61 | } 62 | fmt.Printf("channel#%d: %d\n", j, v) 63 | } 64 | 65 | } 66 | 67 | func testTee() { 68 | fmt.Println("tee:") 69 | a := channels.NewNativeChannel(channels.None) 70 | outputs := []channels.Channel{ 71 | channels.NewNativeChannel(channels.None), 72 | channels.NewNativeChannel(channels.None), 73 | channels.NewNativeChannel(channels.None), 74 | channels.NewNativeChannel(channels.None), 75 | } 76 | 77 | channels.Tee(a, outputs[0], outputs[1], outputs[2], outputs[3]) 78 | //channels.WeakTee(a, outputs[0], outputs[1], outputs[2], outputs[3]) 79 | 80 | go func() { 81 | for i := 0; i < 5; i++ { 82 | a.In() <- i 83 | } 84 | a.Close() 85 | }() 86 | 87 | for i := 0; i < 20; i++ { 88 | var v interface{} 89 | var j int 90 | select { 91 | case v = <-outputs[0].Out(): 92 | j = 0 93 | case v = <-outputs[1].Out(): 94 | j = 1 95 | case v = <-outputs[2].Out(): 96 | j = 2 97 | case v = <-outputs[3].Out(): 98 | j = 3 99 | } 100 | fmt.Printf("channel#%d: %d\n", j, v) 101 | } 102 | } 103 | 104 | func testMulti() { 105 | fmt.Println("multi:") 106 | a := channels.NewNativeChannel(channels.None) 107 | inputs := []channels.Channel{ 108 | channels.NewNativeChannel(channels.None), 109 | channels.NewNativeChannel(channels.None), 110 | channels.NewNativeChannel(channels.None), 111 | channels.NewNativeChannel(channels.None), 112 | } 113 | 114 | channels.Multiplex(a, inputs[0], inputs[1], inputs[2], inputs[3]) 115 | //channels.WeakMultiplex(a, inputs[0], inputs[1], inputs[2], inputs[3]) 116 | 117 | go func() { 118 | for i := 0; i < 5; i++ { 119 | for j := range inputs { 120 | inputs[j].In() <- i 121 | } 122 | } 123 | for i := range inputs { 124 | inputs[i].Close() 125 | } 126 | }() 127 | 128 | for v := range a.Out() { 129 | fmt.Printf("%d ", v) 130 | } 131 | } 132 | 133 | func main() { 134 | testPipe() 135 | testDist() 136 | testTee() 137 | testMulti() 138 | } 139 | -------------------------------------------------------------------------------- /fanin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "sync" 7 | ) 8 | 9 | // https://github.com/campoy/justforfunc/blob/master/27-merging-chans/main.go 10 | 11 | func fanIn(chans ...<-chan interface{}) <-chan interface{} { 12 | out := make(chan interface{}) 13 | go func() { 14 | var wg sync.WaitGroup 15 | wg.Add(len(chans)) 16 | 17 | for _, c := range chans { 18 | go func(c <-chan interface{}) { 19 | for v := range c { 20 | out <- v 21 | } 22 | wg.Done() 23 | }(c) 24 | } 25 | 26 | wg.Wait() 27 | close(out) 28 | }() 29 | return out 30 | } 31 | 32 | func fanInReflect(chans ...<-chan interface{}) <-chan interface{} { 33 | out := make(chan interface{}) 34 | go func() { 35 | defer close(out) 36 | var cases []reflect.SelectCase 37 | for _, c := range chans { 38 | cases = append(cases, reflect.SelectCase{ 39 | Dir: reflect.SelectRecv, 40 | Chan: reflect.ValueOf(c), 41 | }) 42 | } 43 | 44 | for len(cases) > 0 { 45 | i, v, ok := reflect.Select(cases) 46 | if !ok { //remove this case 47 | cases = append(cases[:i], cases[i+1:]...) 48 | continue 49 | } 50 | out <- v.Interface() 51 | } 52 | }() 53 | return out 54 | 55 | } 56 | 57 | func fanInRec(chans ...<-chan interface{}) <-chan interface{} { 58 | switch len(chans) { 59 | case 0: 60 | c := make(chan interface{}) 61 | close(c) 62 | return c 63 | case 1: 64 | return chans[0] 65 | case 2: 66 | return mergeTwo(chans[0], chans[1]) 67 | default: 68 | m := len(chans) / 2 69 | return mergeTwo( 70 | fanInRec(chans[:m]...), 71 | fanInRec(chans[m:]...)) 72 | } 73 | } 74 | 75 | func mergeTwo(a, b <-chan interface{}) <-chan interface{} { 76 | c := make(chan interface{}) 77 | 78 | go func() { 79 | defer close(c) 80 | for a != nil || b != nil { 81 | select { 82 | case v, ok := <-a: 83 | if !ok { 84 | a = nil 85 | continue 86 | } 87 | c <- v 88 | case v, ok := <-b: 89 | if !ok { 90 | b = nil 91 | continue 92 | } 93 | c <- v 94 | } 95 | } 96 | }() 97 | return c 98 | } 99 | 100 | func asStream(done <-chan struct{}) <-chan interface{} { 101 | s := make(chan interface{}) 102 | values := []int{1, 2, 3, 4, 5} 103 | go func() { 104 | defer close(s) 105 | 106 | for _, v := range values { 107 | select { 108 | case <-done: 109 | return 110 | case s <- v: 111 | } 112 | } 113 | 114 | }() 115 | return s 116 | } 117 | 118 | func main() { 119 | fmt.Println("fanIn by goroutine:") 120 | done := make(chan struct{}) 121 | ch := fanIn(asStream(done), asStream(done), asStream(done)) 122 | for v := range ch { 123 | fmt.Println(v) 124 | } 125 | 126 | fmt.Println("fanIn by reflect:") 127 | ch = fanInReflect(asStream(done), asStream(done), asStream(done)) 128 | for v := range ch { 129 | fmt.Println(v) 130 | } 131 | 132 | fmt.Println("fanIn by recursion:") 133 | ch = fanInRec(asStream(done), asStream(done), asStream(done)) 134 | for v := range ch { 135 | fmt.Println(v) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /fanout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func fanOut(ch <-chan interface{}, out []chan interface{}, async bool) { 9 | go func() { 10 | defer func() { 11 | for i := 0; i < len(out); i++ { 12 | close(out[i]) 13 | } 14 | }() 15 | 16 | for v := range ch { 17 | v := v 18 | for i := 0; i < len(out); i++ { 19 | i := i 20 | if async { 21 | go func() { 22 | out[i] <- v 23 | }() 24 | } else { 25 | out[i] <- v 26 | } 27 | } 28 | } 29 | }() 30 | } 31 | 32 | func fanOutReflect(ch <-chan interface{}, out []chan interface{}) { 33 | go func() { 34 | defer func() { 35 | for i := 0; i < len(out); i++ { 36 | close(out[i]) 37 | } 38 | }() 39 | 40 | cases := make([]reflect.SelectCase, len(out)) 41 | for i := range cases { 42 | cases[i].Dir = reflect.SelectSend 43 | } 44 | 45 | for v := range ch { 46 | v := v 47 | for i := range cases { 48 | cases[i].Chan = reflect.ValueOf(out[i]) 49 | cases[i].Send = reflect.ValueOf(v) 50 | } 51 | 52 | for _ = range cases { // for each channel 53 | chosen, _, _ := reflect.Select(cases) 54 | cases[chosen].Chan = reflect.ValueOf(nil) 55 | } 56 | } 57 | }() 58 | } 59 | 60 | func asStream(done <-chan struct{}) <-chan interface{} { 61 | s := make(chan interface{}) 62 | values := []int{1, 2, 3, 4, 5} 63 | go func() { 64 | defer close(s) 65 | 66 | for _, v := range values { 67 | select { 68 | case <-done: 69 | return 70 | case s <- v: 71 | } 72 | } 73 | 74 | }() 75 | return s 76 | } 77 | 78 | func main() { 79 | source := asStream(nil) 80 | channels := make([]chan interface{}, 5) 81 | 82 | fmt.Println("fanOut") 83 | for i := 0; i < 5; i++ { 84 | channels[i] = make(chan interface{}) 85 | } 86 | fanOut(source, channels, false) 87 | for i := 0; i < 5; i++ { 88 | for j := 0; j < 5; j++ { 89 | fmt.Printf("channel#%d: %v\n", j, <-channels[j]) 90 | } 91 | } 92 | 93 | fmt.Println("fanOut By Reflect") 94 | source = asStream(nil) 95 | for i := 0; i < 5; i++ { 96 | channels[i] = make(chan interface{}) 97 | } 98 | fanOutReflect(source, channels) 99 | for i := 0; i < 5; i++ { 100 | for j := 0; j < 5; j++ { 101 | fmt.Printf("channel#%d: %v\n", j, <-channels[j]) 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /fanout2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | func fanOut(ch <-chan interface{}, out []chan interface{}) { 10 | go func() { 11 | defer func() { 12 | for i := 0; i < len(out); i++ { 13 | close(out[i]) 14 | } 15 | }() 16 | 17 | // roundrobin 18 | var i = 0 19 | var n = len(out) 20 | for v := range ch { 21 | v := v 22 | out[i] <- v 23 | i = (i + 1) % n 24 | } 25 | }() 26 | } 27 | 28 | func fanOutReflect(ch <-chan interface{}, out []chan interface{}) { 29 | go func() { 30 | defer func() { 31 | for i := 0; i < len(out); i++ { 32 | close(out[i]) 33 | } 34 | }() 35 | 36 | cases := make([]reflect.SelectCase, len(out)) 37 | for i := range cases { 38 | cases[i].Dir = reflect.SelectSend 39 | cases[i].Chan = reflect.ValueOf(out[i]) 40 | 41 | } 42 | 43 | for v := range ch { 44 | v := v 45 | for i := range cases { 46 | cases[i].Send = reflect.ValueOf(v) 47 | } 48 | _, _, _ = reflect.Select(cases) 49 | } 50 | }() 51 | } 52 | 53 | func asStream(done <-chan struct{}) <-chan interface{} { 54 | s := make(chan interface{}) 55 | values := []int{1, 2, 3, 4, 5} 56 | go func() { 57 | defer close(s) 58 | 59 | for _, v := range values { 60 | select { 61 | case <-done: 62 | return 63 | case s <- v: 64 | } 65 | } 66 | 67 | }() 68 | return s 69 | } 70 | 71 | func main() { 72 | done := make(chan struct{}) 73 | source := asStream(done) 74 | channels := make([]chan interface{}, 5) 75 | 76 | fmt.Println("fanOut") 77 | for i := 0; i < 5; i++ { 78 | channels[i] = make(chan interface{}) 79 | } 80 | fanOut(source, channels) 81 | for i := 0; i < 5; i++ { 82 | i := i 83 | go func() { 84 | for j := 0; j < 5; j++ { 85 | v, ok := <-channels[i] 86 | if ok { 87 | fmt.Printf("channel#%d: %v\n", i, v) 88 | } 89 | 90 | } 91 | }() 92 | } 93 | time.Sleep(time.Second) 94 | close(done) 95 | 96 | fmt.Println("fanOut By Reflect") 97 | done = make(chan struct{}) 98 | source = asStream(done) 99 | for i := 0; i < 5; i++ { 100 | channels[i] = make(chan interface{}) 101 | } 102 | fanOutReflect(source, channels) 103 | for i := 0; i < 5; i++ { 104 | i := i 105 | go func() { 106 | for j := 0; j < 5; j++ { 107 | v, ok := <-channels[i] 108 | if ok { 109 | fmt.Printf("channel#%d: %v\n", i, v) 110 | } 111 | } 112 | }() 113 | } 114 | time.Sleep(time.Second) 115 | close(done) 116 | 117 | } 118 | -------------------------------------------------------------------------------- /flat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func orDone(done <-chan struct{}, c <-chan interface{}) <-chan interface{} { 8 | valStream := make(chan interface{}) 9 | go func() { 10 | defer close(valStream) 11 | for { 12 | select { 13 | case <-done: 14 | return 15 | case v, ok := <-c: 16 | if ok == false { 17 | return 18 | } 19 | select { 20 | case valStream <- v: 21 | case <-done: 22 | } 23 | } 24 | } 25 | }() 26 | return valStream 27 | } 28 | func flat(done <-chan struct{}, chanStream <-chan <-chan interface{}) <-chan interface{} { 29 | valStream := make(chan interface{}) 30 | go func() { 31 | defer close(valStream) 32 | for { 33 | var stream <-chan interface{} 34 | select { 35 | case maybeStream, ok := <-chanStream: 36 | if ok == false { 37 | return 38 | } 39 | stream = maybeStream 40 | case <-done: 41 | return 42 | } 43 | for val := range orDone(done, stream) { 44 | select { 45 | case valStream <- val: 46 | case <-done: 47 | } 48 | } 49 | } 50 | }() 51 | return valStream 52 | } 53 | 54 | func main() { 55 | 56 | genVals := func() <-chan <-chan interface{} { 57 | chanStream := make(chan (<-chan interface{})) 58 | go func() { 59 | defer close(chanStream) 60 | for i := 0; i < 10; i++ { 61 | stream := make(chan interface{}, 1) 62 | stream <- i 63 | close(stream) 64 | chanStream <- stream 65 | } 66 | }() 67 | return chanStream 68 | } 69 | 70 | for v := range flat(nil, genVals()) { 71 | fmt.Printf("%v ", v) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /mapreduce/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func mapChan(in <-chan interface{}, fn func(interface{}) interface{}) <-chan interface{} { 6 | out := make(chan interface{}) 7 | if in == nil { 8 | close(out) 9 | return out 10 | } 11 | 12 | go func() { 13 | defer close(out) 14 | 15 | for v := range in { 16 | out <- fn(v) 17 | } 18 | }() 19 | 20 | return out 21 | } 22 | 23 | func reduce(in <-chan interface{}, fn func(r, v interface{}) interface{}) interface{} { 24 | if in == nil { 25 | return nil 26 | } 27 | 28 | out := <-in 29 | for v := range in { 30 | out = fn(out, v) 31 | } 32 | 33 | return out 34 | } 35 | 36 | func asStream(done <-chan struct{}) <-chan interface{} { 37 | s := make(chan interface{}) 38 | values := []int{1, 2, 3, 4, 5} 39 | go func() { 40 | defer close(s) 41 | 42 | for _, v := range values { 43 | select { 44 | case <-done: 45 | return 46 | case s <- v: 47 | } 48 | } 49 | 50 | }() 51 | return s 52 | } 53 | 54 | func main() { 55 | in := asStream(nil) 56 | 57 | // map op: time 10 58 | mapFn := func(v interface{}) interface{} { 59 | return v.(int) * 10 60 | } 61 | 62 | // reduce op: sum 63 | reduceFn := func(r, v interface{}) interface{} { 64 | return r.(int) + v.(int) 65 | } 66 | 67 | sum := reduce(mapChan(in, mapFn), reduceFn) 68 | fmt.Println(sum) 69 | } 70 | -------------------------------------------------------------------------------- /or_channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // refer to https://github.com/kat-co/concurrency-in-go-src/blob/master/concurrency-patterns-in-go/the-or-channel/fig-or-channel.go 9 | func or(channels ...<-chan interface{}) <-chan interface{} { 10 | switch len(channels) { 11 | case 0: 12 | return nil 13 | case 1: 14 | return channels[0] 15 | } 16 | 17 | orDone := make(chan interface{}) 18 | go func() { 19 | defer close(orDone) 20 | 21 | switch len(channels) { 22 | case 2: 23 | select { 24 | case <-channels[0]: 25 | case <-channels[1]: 26 | } 27 | default: 28 | select { 29 | case <-channels[0]: 30 | case <-channels[1]: 31 | case <-channels[2]: 32 | case <-or(append(channels[3:], orDone)...): 33 | } 34 | } 35 | }() 36 | return orDone 37 | } 38 | 39 | func sig(after time.Duration) <-chan interface{} { 40 | c := make(chan interface{}) 41 | go func() { 42 | defer close(c) 43 | time.Sleep(after) 44 | }() 45 | return c 46 | } 47 | 48 | func main() { 49 | 50 | start := time.Now() 51 | 52 | <-or( 53 | sig(10*time.Second), 54 | sig(20*time.Second), 55 | sig(30*time.Second), 56 | sig(40*time.Second), 57 | sig(50*time.Second), 58 | sig(01*time.Minute), 59 | ) 60 | 61 | fmt.Printf("done after %v", time.Since(start)) 62 | } 63 | -------------------------------------------------------------------------------- /or_channel_go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func or(chans ...<-chan interface{}) <-chan interface{} { 10 | out := make(chan interface{}) 11 | go func() { 12 | var once sync.Once 13 | for _, c := range chans { 14 | go func(c <-chan interface{}) { 15 | select { 16 | case <-c: 17 | once.Do(func() { close(out) }) 18 | case <-out: 19 | } 20 | }(c) 21 | } 22 | }() 23 | return out 24 | } 25 | 26 | func main() { 27 | sig := func(after time.Duration) <-chan interface{} { 28 | c := make(chan interface{}) 29 | go func() { 30 | defer close(c) 31 | time.Sleep(after) 32 | }() 33 | return c 34 | } 35 | 36 | start := time.Now() 37 | 38 | <-or( 39 | sig(10*time.Second), 40 | sig(10*time.Second), 41 | sig(10*time.Second), 42 | sig(10*time.Second), 43 | sig(10*time.Second), 44 | sig(01*time.Minute), 45 | ) 46 | 47 | fmt.Printf("done after %v", time.Since(start)) 48 | } 49 | -------------------------------------------------------------------------------- /or_channel_rec/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func or(channels ...<-chan interface{}) <-chan interface{} { 9 | switch len(channels) { 10 | case 0: 11 | return nil 12 | case 1: 13 | return channels[0] 14 | } 15 | 16 | orDone := make(chan interface{}) 17 | go func() { 18 | defer close(orDone) 19 | 20 | switch len(channels) { 21 | case 2: 22 | select { 23 | case <-channels[0]: 24 | case <-channels[1]: 25 | } 26 | default: 27 | m := len(channels) / 2 28 | select { 29 | case <-or(channels[:m]...): 30 | case <-or(channels[m:]...): 31 | } 32 | } 33 | }() 34 | 35 | return orDone 36 | } 37 | 38 | func sig(after time.Duration) <-chan interface{} { 39 | c := make(chan interface{}) 40 | go func() { 41 | defer close(c) 42 | time.Sleep(after) 43 | }() 44 | return c 45 | } 46 | 47 | func main() { 48 | 49 | start := time.Now() 50 | 51 | <-or( 52 | sig(10*time.Second), 53 | sig(20*time.Second), 54 | sig(30*time.Second), 55 | sig(40*time.Second), 56 | sig(50*time.Second), 57 | sig(01*time.Minute), 58 | ) 59 | 60 | fmt.Printf("done after %v", time.Since(start)) 61 | } 62 | -------------------------------------------------------------------------------- /or_channel_reflect/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "time" 7 | ) 8 | 9 | func or(channels ...<-chan interface{}) <-chan interface{} { 10 | switch len(channels) { 11 | case 0: 12 | return nil 13 | case 1: 14 | return channels[0] 15 | } 16 | 17 | orDone := make(chan interface{}) 18 | go func() { 19 | defer close(orDone) 20 | var cases []reflect.SelectCase 21 | for _, c := range channels { 22 | cases = append(cases, reflect.SelectCase{ 23 | Dir: reflect.SelectRecv, 24 | Chan: reflect.ValueOf(c), 25 | }) 26 | } 27 | 28 | reflect.Select(cases) 29 | }() 30 | 31 | return orDone 32 | } 33 | 34 | func sig(after time.Duration) <-chan interface{} { 35 | c := make(chan interface{}) 36 | go func() { 37 | defer close(c) 38 | time.Sleep(after) 39 | }() 40 | return c 41 | } 42 | 43 | func main() { 44 | 45 | start := time.Now() 46 | 47 | <-or( 48 | sig(10*time.Second), 49 | sig(20*time.Second), 50 | sig(30*time.Second), 51 | sig(40*time.Second), 52 | sig(50*time.Second), 53 | sig(01*time.Minute), 54 | ) 55 | 56 | fmt.Printf("done after %v", time.Since(start)) 57 | } 58 | -------------------------------------------------------------------------------- /or_done_channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func orDone(done <-chan struct{}, c <-chan interface{}) <-chan interface{} { 6 | valStream := make(chan interface{}) 7 | go func() { 8 | defer close(valStream) 9 | for { 10 | select { 11 | case <-done: 12 | return 13 | case v, ok := <-c: 14 | if ok == false { 15 | return 16 | } 17 | select { 18 | case valStream <- v: 19 | case <-done: 20 | } 21 | } 22 | } 23 | }() 24 | return valStream 25 | } 26 | 27 | func main() { 28 | ch := make(chan interface{}) 29 | go func() { 30 | defer close(ch) 31 | 32 | for i := 0; i < 10; i++ { 33 | ch <- i 34 | } 35 | }() 36 | 37 | for v := range orDone(nil, ch) { 38 | fmt.Printf("%v ", v) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /perf/chan_test.go: -------------------------------------------------------------------------------- 1 | package chans 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkChan_WithTwoCases(b *testing.B) { 9 | ch := make(chan struct{}, 1000) 10 | done := make(chan struct{}) 11 | go func() { 12 | for { 13 | select { 14 | case <-done: 15 | return 16 | case <-ch: 17 | } 18 | } 19 | }() 20 | 21 | for i := 0; i < b.N; i++ { 22 | ch <- struct{}{} 23 | } 24 | close(done) 25 | } 26 | 27 | func BenchmarkChan_WithOneCase(b *testing.B) { 28 | ch := make(chan struct{}, 1000) 29 | go func() { 30 | for { 31 | select { 32 | case <-ch: 33 | } 34 | } 35 | }() 36 | 37 | for i := 0; i < b.N; i++ { 38 | ch <- struct{}{} 39 | } 40 | } 41 | 42 | func BenchmarkChan_WithContext(b *testing.B) { 43 | ch := make(chan struct{}, 1000) 44 | ctx, cancel := context.WithCancel(context.Background()) 45 | go func() { 46 | done := ctx.Done() 47 | for { 48 | select { 49 | case <-done: 50 | case <-ch: 51 | } 52 | } 53 | }() 54 | 55 | for i := 0; i < b.N; i++ { 56 | ch <- struct{}{} 57 | } 58 | 59 | cancel() 60 | } 61 | 62 | func BenchmarkChan_Range(b *testing.B) { 63 | ch := make(chan struct{}, 1000) 64 | go func() { 65 | for range ch { 66 | } 67 | }() 68 | 69 | for i := 0; i < b.N; i++ { 70 | ch <- struct{}{} 71 | } 72 | } 73 | 74 | func BenchmarkChan_WithoutCase(b *testing.B) { 75 | ch := make(chan struct{}, 1000) 76 | go func() { 77 | for { 78 | <-ch 79 | } 80 | }() 81 | 82 | for i := 0; i < b.N; i++ { 83 | ch <- struct{}{} 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /pipeline/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func sq(in <-chan int) <-chan int { 6 | out := make(chan int) 7 | go func() { 8 | for n := range in { 9 | out <- n * n 10 | } 11 | close(out) 12 | }() 13 | return out 14 | } 15 | 16 | func main() { 17 | // Set up the pipeline. 18 | c := gen(2, 3) 19 | out := sq(c) 20 | 21 | // Consume the output. 22 | fmt.Println(<-out) // 4 23 | fmt.Println(<-out) // 9 24 | } 25 | 26 | func gen(nums ...int) <-chan int { 27 | out := make(chan int) 28 | go func() { 29 | for _, n := range nums { 30 | out <- n 31 | } 32 | close(out) 33 | }() 34 | return out 35 | } 36 | -------------------------------------------------------------------------------- /stream/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func asStream(done <-chan struct{}, values ...interface{}) <-chan interface{} { 8 | s := make(chan interface{}) 9 | go func() { 10 | defer close(s) 11 | 12 | for _, v := range values { 13 | select { 14 | case <-done: 15 | return 16 | case s <- v: 17 | } 18 | } 19 | 20 | }() 21 | return s 22 | } 23 | func asRepeatStream(done <-chan struct{}, values ...interface{}) <-chan interface{} { 24 | s := make(chan interface{}) 25 | go func() { 26 | defer close(s) 27 | for { 28 | for _, v := range values { 29 | select { 30 | case <-done: 31 | return 32 | case s <- v: 33 | } 34 | } 35 | } 36 | }() 37 | return s 38 | } 39 | 40 | func takeN(done <-chan struct{}, valueStream <-chan interface{}, num int) <-chan interface{} { 41 | takeStream := make(chan interface{}) 42 | go func() { 43 | defer close(takeStream) 44 | for i := 0; i < num; i++ { 45 | select { 46 | case <-done: 47 | return 48 | case takeStream <- <-valueStream: 49 | } 50 | } 51 | }() 52 | return takeStream 53 | } 54 | 55 | func takeFn(done <-chan struct{}, valueStream <-chan interface{}, fn func(interface{}) bool) <-chan interface{} { 56 | takeStream := make(chan interface{}) 57 | go func() { 58 | defer close(takeStream) 59 | for { 60 | select { 61 | case <-done: 62 | return 63 | case v := <-valueStream: 64 | if fn(v) { 65 | takeStream <- v 66 | } 67 | } 68 | } 69 | }() 70 | return takeStream 71 | } 72 | 73 | func takeWhile(done <-chan struct{}, valueStream <-chan interface{}, fn func(interface{}) bool) <-chan interface{} { 74 | takeStream := make(chan interface{}) 75 | go func() { 76 | defer close(takeStream) 77 | for { 78 | select { 79 | case <-done: 80 | return 81 | case v := <-valueStream: 82 | if !fn(v) { 83 | return 84 | } 85 | takeStream <- v 86 | } 87 | } 88 | }() 89 | return takeStream 90 | } 91 | 92 | func skipN(done <-chan struct{}, valueStream <-chan interface{}, num int) <-chan interface{} { 93 | takeStream := make(chan interface{}) 94 | go func() { 95 | defer close(takeStream) 96 | for i := 0; i < num; i++ { 97 | select { 98 | case <-done: 99 | return 100 | case <-valueStream: 101 | } 102 | } 103 | for { 104 | select { 105 | case <-done: 106 | return 107 | case takeStream <- <-valueStream: 108 | } 109 | } 110 | }() 111 | 112 | return takeStream 113 | } 114 | 115 | func skipFn(done <-chan struct{}, valueStream <-chan interface{}, fn func(interface{}) bool) <-chan interface{} { 116 | takeStream := make(chan interface{}) 117 | go func() { 118 | defer close(takeStream) 119 | for { 120 | select { 121 | case <-done: 122 | return 123 | case v := <-valueStream: 124 | if !fn(v) { 125 | takeStream <- v 126 | } 127 | } 128 | } 129 | }() 130 | return takeStream 131 | } 132 | 133 | func skipWhile(done <-chan struct{}, valueStream <-chan interface{}, fn func(interface{}) bool) <-chan interface{} { 134 | takeStream := make(chan interface{}) 135 | go func() { 136 | defer close(takeStream) 137 | take := false 138 | for { 139 | select { 140 | case <-done: 141 | return 142 | case v := <-valueStream: 143 | if !take { 144 | take = !fn(v) 145 | if !take { 146 | continue 147 | } 148 | } 149 | takeStream <- v 150 | } 151 | } 152 | }() 153 | return takeStream 154 | } 155 | 156 | func main() { 157 | fmt.Println("asStream:") 158 | done := make(chan struct{}) 159 | ch := asStream(done, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 160 | for v := range ch { 161 | fmt.Println(v) 162 | } 163 | 164 | fmt.Println("asRepeatStream:") 165 | done = make(chan struct{}) 166 | ch = asRepeatStream(done, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 167 | i := 0 168 | for v := range ch { 169 | fmt.Println(v) 170 | i++ 171 | if i == 20 { 172 | break 173 | } 174 | } 175 | 176 | fmt.Println("takeN:") 177 | done = make(chan struct{}) 178 | for v := range takeN(done, asRepeatStream(done, 1, 2, 3), 6) { 179 | fmt.Println(v) 180 | } 181 | 182 | evenFn := func(v interface{}) bool { 183 | return v.(int)%2 == 0 184 | } 185 | lessFn := func(v interface{}) bool { 186 | return v.(int) < 3 187 | } 188 | 189 | fmt.Println("takeFn:") 190 | done = make(chan struct{}) 191 | i = 0 192 | for v := range takeFn(done, asRepeatStream(done, 1, 2, 3), evenFn) { 193 | fmt.Println(v) 194 | i++ 195 | if i == 20 { 196 | break 197 | } 198 | } 199 | 200 | fmt.Println("takeWhile:") 201 | done = make(chan struct{}) 202 | for v := range takeWhile(done, asRepeatStream(done, 1, 2, 3), lessFn) { 203 | fmt.Println(v) 204 | } 205 | 206 | fmt.Println("skipN:") 207 | done = make(chan struct{}) 208 | for v := range takeN(done, skipN(done, asRepeatStream(done, 1, 2, 3), 2), 4) { 209 | fmt.Println(v) 210 | } 211 | 212 | fmt.Println("skipFn:") 213 | done = make(chan struct{}) 214 | for v := range takeN(done, skipFn(done, asRepeatStream(done, 1, 2, 3), evenFn), 4) { 215 | fmt.Println(v) 216 | } 217 | 218 | fmt.Println("skipWhile:") 219 | done = make(chan struct{}) 220 | for v := range takeN(done, skipWhile(done, asRepeatStream(done, 1, 2, 3), lessFn), 4) { 221 | fmt.Println(v) 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /trylock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "unsafe" 8 | ) 9 | 10 | // defined in sync.Mutex 11 | const mutexLocked = 1 << iota 12 | 13 | type Mutex struct { 14 | mu sync.Mutex 15 | } 16 | 17 | func (m *Mutex) Lock() { 18 | m.mu.Lock() 19 | } 20 | 21 | func (m *Mutex) Unlock() { 22 | m.mu.Unlock() 23 | } 24 | 25 | func (m *Mutex) TryLock() bool { 26 | return atomic.CompareAndSwapInt32((*int32)(unsafe.Pointer(&m.mu)), 0, mutexLocked) 27 | } 28 | 29 | func (m *Mutex) IsLocked() bool { 30 | return atomic.LoadInt32((*int32)(unsafe.Pointer(&m.mu))) == mutexLocked 31 | } 32 | 33 | func main() { 34 | var m Mutex 35 | if m.TryLock() { 36 | fmt.Println("locked: ", m.IsLocked()) 37 | m.Unlock() 38 | } else { 39 | fmt.Println("failed to lock") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /trylock_channel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Mutex struct { 8 | ch chan struct{} 9 | } 10 | 11 | func NewMutex() *Mutex { 12 | mu := &Mutex{make(chan struct{}, 1)} 13 | mu.ch <- struct{}{} 14 | return mu 15 | } 16 | 17 | func (m *Mutex) Lock() { 18 | <-m.ch 19 | } 20 | 21 | func (m *Mutex) Unlock() { 22 | select { 23 | case m.ch <- struct{}{}: 24 | default: 25 | panic("unlock of unlocked mutex") 26 | } 27 | } 28 | 29 | func (m *Mutex) TryLock() bool { 30 | select { 31 | case <-m.ch: 32 | return true 33 | default: 34 | } 35 | return false 36 | } 37 | 38 | func (m *Mutex) IsLocked() bool { 39 | return len(m.ch) == 0 40 | } 41 | 42 | func main() { 43 | m := NewMutex() 44 | ok := m.TryLock() 45 | fmt.Printf("locked v %v\n", ok) 46 | ok = m.TryLock() 47 | fmt.Printf("locked %v\n", ok) 48 | } 49 | -------------------------------------------------------------------------------- /trylock_channel2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Mutex struct { 8 | ch chan struct{} 9 | } 10 | 11 | func NewMutex() *Mutex { 12 | mu := &Mutex{make(chan struct{}, 1)} 13 | return mu 14 | } 15 | 16 | func (m *Mutex) Lock() { 17 | m.ch <- struct{}{} 18 | } 19 | 20 | func (m *Mutex) Unlock() { 21 | select { 22 | case <-m.ch: 23 | default: 24 | panic("unlock of unlocked mutex") 25 | } 26 | } 27 | 28 | func (m *Mutex) TryLock() bool { 29 | select { 30 | case m.ch <- struct{}{}: 31 | return true 32 | default: 33 | } 34 | return false 35 | } 36 | 37 | func (m *Mutex) IsLocked() bool { 38 | return len(m.ch) == 1 39 | } 40 | 41 | func main() { 42 | m := NewMutex() 43 | ok := m.TryLock() 44 | fmt.Printf("locked v %v\n", ok) 45 | ok = m.TryLock() 46 | fmt.Printf("locked %v\n", ok) 47 | } 48 | -------------------------------------------------------------------------------- /trylock_timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Mutex struct { 9 | ch chan struct{} 10 | } 11 | 12 | func NewMutex() *Mutex { 13 | mu := &Mutex{make(chan struct{}, 1)} 14 | mu.ch <- struct{}{} 15 | return mu 16 | } 17 | 18 | func (m *Mutex) Lock() { 19 | <-m.ch 20 | } 21 | 22 | func (m *Mutex) Unlock() { 23 | select { 24 | case m.ch <- struct{}{}: 25 | default: 26 | panic("unlock of unlocked mutex") 27 | } 28 | } 29 | 30 | func (m *Mutex) TryLock(timeout time.Duration) bool { 31 | timer := time.NewTimer(timeout) 32 | select { 33 | case <-m.ch: 34 | timer.Stop() 35 | return true 36 | case <-timer.C: 37 | } 38 | return false 39 | } 40 | 41 | func (m *Mutex) IsLocked() bool { 42 | return len(m.ch) == 0 43 | } 44 | 45 | func main() { 46 | m := NewMutex() 47 | ok := m.TryLock(time.Second) 48 | fmt.Printf("locked v %v\n", ok) 49 | ok = m.TryLock(time.Second) 50 | fmt.Printf("locked %v\n", ok) 51 | } 52 | --------------------------------------------------------------------------------