├── README.md ├── buffer_size ├── hi_fan_buffered.go ├── hi_test.go ├── result.txt └── run.sh ├── fan_model_slow ├── hi_fan.go ├── hi_fan_example.go └── hi_simple.go ├── hi_fan.go ├── hi_fan_example.go ├── hi_simple.go ├── master ├── hi_fan.go ├── hi_fan_example.go └── hi_simple.go └── optimize_fan_model ├── hi_fan.go ├── hi_fan_buffered.go ├── hi_fan_example.go └── hi_simple.go /README.md: -------------------------------------------------------------------------------- 1 | # 目录介绍 2 | - master:主要代码 3 | - fan_model_slow:介绍fan不一定能提升性能 4 | - optimize_fan_model:优化FAN模式的代码 5 | - buffer_size:对buffer缓冲区大小的测试 6 | 7 | ======================== 8 | 9 | 前一篇文章[《Golang并发模型:轻松入门流水线模型》](https://segmentfault.com/a/1190000017142506),介绍了流水线模型的概念,这篇文章是流水线模型进阶,介绍FAN-IN和FAN-OUT,FAN模式可以让我们的流水线模型更好的利用Golang并发,提高软件性能。但FAN模式不一定是万能,不见得能提高程序的性能,甚至还不如普通的流水线。我们先介绍下FAN模式,再看看它怎么提升性能的,它是不是万能的。 10 | 11 | 12 | 13 | # FAN-IN和FAN-OUT模式 14 | 15 | Golang的并发模式灵感来自现实世界,这些模式是通用的,毫无例外,FAN模式也是对当前世界的模仿。**以汽车组装为例,汽车生产线上有个阶段是给小汽车装4个轮子,可以把这个阶段任务交给4个人同时去做,这4个人把轮子都装完后,再把汽车移动到生产线下一个阶段。这个过程中,就有任务的分发,和任务结果的收集。其中任务分发是FAN-OUT,任务收集是FAN-IN。** 16 | 17 | - **FAN-OUT模式:多个goroutine从同一个通道读取数据,直到该通道关闭。**OUT是一种张开的模式,所以又被称为扇出,可以用来分发任务。 18 | - **FAN-IN模式:1个goroutine从多个通道读取数据,直到这些通道关闭。**IN是一种收敛的模式,所以又被称为扇入,用来收集处理的结果。 19 | 20 | ![fan-in和fan-out.png](https://upload-images.jianshu.io/upload_images/10901752-727b047a9808439d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 21 | 22 | 23 | # FAN-IN和FAN-OUT实践 24 | 25 | 我们这次试用FAN-OUT和FAN-IN,解决[《Golang并发模型:轻松入门流水线模型》](https://segmentfault.com/a/1190000017142506?_ea=5178632)中提到的问题:计算一个整数切片中元素的平方值并把它打印出来。 26 | 27 | - `producer()`保持不变,负责生产数据。 28 | - `squre()`也不变,负责计算平方值。 29 | - 修改`main()`,启动3个square,这3个squre从producer生成的通道读数据,**这是FAN-OUT**。 30 | - 增加`merge()`,入参是3个square各自写数据的通道,给这3个通道分别启动1个协程,把数据写入到自己创建的通道,并返回该通道,**这是FAN-IN**。 31 | 32 | [FAN模式流水线示例](https://github.com/Shitaibin/golang_pipeline_step_by_step/blob/master/hi_fan_example.go): 33 | ```go 34 | package main 35 | 36 | import ( 37 | "fmt" 38 | "sync" 39 | ) 40 | 41 | func producer(nums ...int) <-chan int { 42 | out := make(chan int) 43 | go func() { 44 | defer close(out) 45 | for _, n := range nums { 46 | out <- i 47 | } 48 | }() 49 | return out 50 | } 51 | 52 | func square(inCh <-chan int) <-chan int { 53 | out := make(chan int) 54 | go func() { 55 | defer close(out) 56 | for n := range inCh { 57 | out <- n * n 58 | } 59 | }() 60 | 61 | return out 62 | } 63 | 64 | func merge(cs ...<-chan int) <-chan int { 65 | out := make(chan int) 66 | 67 | var wg sync.WaitGroup 68 | 69 | collect := func(in <-chan int) { 70 | defer wg.Done() 71 | for n := range in { 72 | out <- n 73 | } 74 | } 75 | 76 | wg.Add(len(cs)) 77 | // FAN-IN 78 | for _, c := range cs { 79 | go collect(c) 80 | } 81 | 82 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 83 | // wg.Wait() 84 | // close(out) 85 | 86 | // 正确方式 87 | go func() { 88 | wg.Wait() 89 | close(out) 90 | }() 91 | 92 | return out 93 | } 94 | 95 | func main() { 96 | in := producer(1, 2, 3, 4) 97 | 98 | // FAN-OUT 99 | c1 := square(in) 100 | c2 := square(in) 101 | c3 := square(in) 102 | 103 | // consumer 104 | for ret := range merge(c1, c2, c3) { 105 | fmt.Printf("%3d ", ret) 106 | } 107 | fmt.Println() 108 | } 109 | ``` 110 | 111 | 3个squre协程**并发**运行,结果顺序是无法确定的,所以你得到的结果,不一定与下面的相同。 112 | 113 | ```go 114 | ➜ awesome git:(master) ✗ go run hi.go 115 | 1 4 16 9 116 | ``` 117 | 118 | # FAN模式真能提升性能吗? 119 | 120 | 相信你心里已经有了答案,可以的。我们还是使用老问题,对比一下简单的流水线和FAN模式的流水线,修改下代码,增加程序的执行时间: 121 | 122 | - `produer()`使用参数生成指定数量的数据。 123 | - `square()`增加阻塞操作,睡眠1s,模拟阶段的运行时间。 124 | - `main()`关闭对结果数据的打印,降低结果处理时的IO对FAN模式的对比。 125 | 126 | [普通流水线](https://github.com/Shitaibin/golang_pipeline_step_by_step/blob/master/hi_simple.go): 127 | ```go 128 | // hi_simple.go 129 | 130 | package main 131 | 132 | import ( 133 | "fmt" 134 | ) 135 | 136 | func producer(n int) <-chan int { 137 | out := make(chan int) 138 | go func() { 139 | defer close(out) 140 | for i := 0; i < n; i++ { 141 | out <- i 142 | } 143 | }() 144 | return out 145 | } 146 | 147 | func square(inCh <-chan int) <-chan int { 148 | out := make(chan int) 149 | go func() { 150 | defer close(out) 151 | for n := range inCh { 152 | out <- n * n 153 | // simulate 154 | time.Sleep(time.Second) 155 | } 156 | }() 157 | 158 | return out 159 | } 160 | 161 | func main() { 162 | in := producer(10) 163 | ch := square(in) 164 | 165 | // consumer 166 | for _ = range ch { 167 | } 168 | } 169 | ``` 170 | 171 | 172 | 使用[FAN模式的流水线](https://github.com/Shitaibin/golang_pipeline_step_by_step/blob/master/hi_fan.go): 173 | ```go 174 | // hi_fan.go 175 | package main 176 | 177 | import ( 178 | "sync" 179 | "time" 180 | ) 181 | 182 | func producer(n int) <-chan int { 183 | out := make(chan int) 184 | go func() { 185 | defer close(out) 186 | for i := 0; i < n; i++ { 187 | out <- i 188 | } 189 | }() 190 | return out 191 | } 192 | 193 | func square(inCh <-chan int) <-chan int { 194 | out := make(chan int) 195 | go func() { 196 | defer close(out) 197 | for n := range inCh { 198 | out <- n * n 199 | // simulate 200 | time.Sleep(time.Second) 201 | } 202 | }() 203 | 204 | return out 205 | } 206 | 207 | func merge(cs ...<-chan int) <-chan int { 208 | out := make(chan int) 209 | 210 | var wg sync.WaitGroup 211 | 212 | collect := func(in <-chan int) { 213 | defer wg.Done() 214 | for n := range in { 215 | out <- n 216 | } 217 | } 218 | 219 | wg.Add(len(cs)) 220 | // FAN-IN 221 | for _, c := range cs { 222 | go collect(c) 223 | } 224 | 225 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 226 | // wg.Wait() 227 | // close(out) 228 | 229 | // 正确方式 230 | go func() { 231 | wg.Wait() 232 | close(out) 233 | }() 234 | 235 | return out 236 | } 237 | 238 | func main() { 239 | in := producer(10) 240 | 241 | // FAN-OUT 242 | c1 := square(in) 243 | c2 := square(in) 244 | c3 := square(in) 245 | 246 | // consumer 247 | for _ = range merge(c1, c2, c3) { 248 | } 249 | } 250 | ``` 251 | 252 | 253 | 254 | 多次测试,每次结果近似,结果如下: 255 | 256 | - FAN模式利用了7%的CPU,而普通流水线CPU只使用了3%,**FAN模式能够更好的利用CPU,提供更好的并发,提高Golang程序的并发性能。** 257 | - FAN模式耗时10s,普通流水线耗时4s。**在协程比较费时时,FAN模式可以减少程序运行时间,同样的时间,可以处理更多的数据。** 258 | 259 | ```bash 260 | ➜ awesome git:(master) ✗ time go run hi_simple.go 261 | go run hi_simple.go 0.17s user 0.18s system 3% cpu 10.389 total 262 | ➜ awesome git:(master) ✗ 263 | ➜ awesome git:(master) ✗ time go run hi_fan.go 264 | go run hi_fan.go 0.17s user 0.16s system 7% cpu 4.288 total 265 | ``` 266 | 267 | 268 | 269 | **也可以使用Benchmark进行测试,看2个类型的执行时间,结论相同**。为了节约篇幅,这里不再介绍,[方法和结果贴在Gist](https://gist.github.com/Shitaibin/9593a18989b6c81bb3aae5ccdf9b6470)了,想看的朋友瞄一眼,或自己动手搞搞。 270 | 271 | 272 | 273 | # FAN模式一定能提升性能吗? 274 | 275 | FAN模式可以提高并发的性能,那我们是不是可以都使用FAN模式? 276 | 277 | 不行的,因为**FAN模式不一定能提升性能。** 278 | 279 | 依然使用之前的问题,再次修改下代码,其他不变: 280 | 281 | - `squre()`去掉耗时。 282 | - `main()`增加producer()的入参,让producer生产10,000,000个数据。 283 | 284 | [简单版流水线修改代码](https://github.com/Shitaibin/golang_pipeline_step_by_step/blob/fan_model_slow/hi_simple.go): 285 | ```go 286 | // hi_simple.go 287 | 288 | func square(inCh <-chan int) <-chan int { 289 | out := make(chan int) 290 | go func() { 291 | defer close(out) 292 | for n := range inCh { 293 | out <- n * n 294 | } 295 | }() 296 | 297 | return out 298 | } 299 | 300 | func main() { 301 | in := producer(10000000) 302 | ch := square(in) 303 | 304 | // consumer 305 | for _ = range ch { 306 | } 307 | } 308 | ``` 309 | 310 | 311 | [FAN模式流水线修改代码](https://github.com/Shitaibin/golang_pipeline_step_by_step/blob/fan_model_slow/hi_fan.go): 312 | ```go 313 | // hi-fan.go 314 | package main 315 | 316 | import ( 317 | "sync" 318 | ) 319 | 320 | func square(inCh <-chan int) <-chan int { 321 | out := make(chan int) 322 | go func() { 323 | defer close(out) 324 | for n := range inCh { 325 | out <- n * n 326 | } 327 | }() 328 | 329 | return out 330 | } 331 | 332 | func main() { 333 | in := producer(10000000) 334 | 335 | // FAN-OUT 336 | c1 := square(in) 337 | c2 := square(in) 338 | c3 := square(in) 339 | 340 | // consumer 341 | for _ = range merge(c1, c2, c3) { 342 | } 343 | } 344 | ``` 345 | 346 | 结果,可以跑多次,结果近似: 347 | 348 | ```bash 349 | ➜ awesome git:(master) ✗ time go run hi-simple.go 350 | go run hi-simple.go 9.96s user 5.93s system 168% cpu 9.424 total 351 | ➜ awesome git:(master) ✗ time go run hi-fan.go 352 | go run hi-fan.go 23.35s user 11.51s system 297% cpu 11.737 total 353 | ``` 354 | 355 | 从这个结果,我们能看到2点。 356 | 357 | - FAN模式可以提高CPU利用率。 358 | - **FAN模式不一定能提升效率,降低程序运行时间。** 359 | 360 | # 优化FAN模式 361 | 362 | 既然FAN模式不一定能提高性能,如何优化? 363 | 364 | **不同的场景优化不同,要依具体的情况,解决程序的瓶颈。** 365 | 366 | 我们当前程序的瓶颈在FAN-IN,squre函数很快就完成,merge函数它把3个数据写入到1个通道的时候出现了瓶颈,**适当使用带缓冲通道可以提高程序性能**,[再修改下代码](https://github.com/Shitaibin/golang_pipeline_step_by_step/blob/optimize_fan_model/hi_fan_buffered.go) 367 | 368 | - `merge()`中的`out`修改为: 369 | 370 | ```go 371 | out := make(chan int, 100) 372 | ``` 373 | 374 | 结果: 375 | 376 | ```bash 377 | ➜ awesome git:(master) ✗ time go run hi_fan-buffer.go 378 | go run hi-fan-buffer.go 19.85s user 8.19s system 323% cpu 8.658 total 379 | ``` 380 | 381 | 使用带缓存通道后,程序的性能有了较大提升,**CPU利用率提高到323%,提升了8%,运行时间从11.7降低到8.6,降低了26%。** 382 | 383 | 384 | FAN模式的特点很简单,相信你已经掌握了,如果记不清了[看这里](#FAN-IN和FAN-OUT模式),本文所有代码浏览该[Github仓库]()。 385 | 386 | FAN模式很有意思,并且能提高Golang并发的性能,如果想以后运用自如,用到自己的项目中去,还是要写写自己的Demo,快去实践一把。 387 | 388 | 389 | 下一篇,写流水线中协程的“优雅退出”,欢迎关注。 390 | 391 | 392 | > 如果这篇文章对你有帮助,请点个赞/喜欢,让我知道我的写作是有价值的,感谢。 393 | 394 | -------------------------------------------------------------------------------- /buffer_size/hi_fan_buffered.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | func producer(n int) <-chan int { 8 | out := make(chan int) 9 | go func() { 10 | defer close(out) 11 | for i := 0; i < n; i++ { 12 | out <- i 13 | } 14 | }() 15 | return out 16 | } 17 | 18 | func square(inCh <-chan int) <-chan int { 19 | out := make(chan int) 20 | go func() { 21 | defer close(out) 22 | for n := range inCh { 23 | out <- n * n 24 | } 25 | }() 26 | 27 | return out 28 | } 29 | 30 | func merge(bufSize int, cs ...<-chan int) <-chan int { 31 | out := make(chan int, bufSize) 32 | 33 | var wg sync.WaitGroup 34 | 35 | collect := func(in <-chan int) { 36 | defer wg.Done() 37 | for n := range in { 38 | out <- n 39 | } 40 | } 41 | 42 | wg.Add(len(cs)) 43 | // FAN-IN 44 | for _, c := range cs { 45 | go collect(c) 46 | } 47 | 48 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 49 | // wg.Wait() 50 | // close(out) 51 | 52 | // 正确方式 53 | go func() { 54 | wg.Wait() 55 | close(out) 56 | }() 57 | 58 | return out 59 | } 60 | 61 | func main() { 62 | in := producer(10000000) 63 | 64 | // FAN-OUT 65 | c1 := square(in) 66 | c2 := square(in) 67 | c3 := square(in) 68 | 69 | // consumer 70 | for _ = range merge(0, c1, c2, c3) { 71 | } 72 | } 73 | 74 | func PipelineFan(bufSize int) { 75 | in := producer(10000000) 76 | 77 | // FAN-OUT 78 | c1 := square(in) 79 | c2 := square(in) 80 | c3 := square(in) 81 | 82 | // consumer 83 | for _ = range merge(bufSize, c1, c2, c3) { 84 | } 85 | } 86 | 87 | func PipelineSimple() { 88 | in := producer(10) 89 | ch := square(in) 90 | 91 | // consumer 92 | for _ = range ch { 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /buffer_size/hi_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "testing" 4 | 5 | func BenchmarkPipelineFan(b *testing.B) { 6 | for i := 0; i < b.N; i++ { 7 | PipelineFan(0) 8 | } 9 | } 10 | 11 | func BenchmarkPipelineSimple(b *testing.B) { 12 | for i := 0; i < b.N; i++ { 13 | PipelineSimple() 14 | } 15 | } 16 | 17 | func BenchmarkPipelineFanBuffered_0(b *testing.B) { 18 | for i := 0; i < b.N; i++ { 19 | PipelineFan(0) 20 | } 21 | } 22 | 23 | func BenchmarkPipelineFanBuffered_1(b *testing.B) { 24 | for i := 0; i < b.N; i++ { 25 | PipelineFan(1) 26 | } 27 | } 28 | 29 | func BenchmarkPipelineFanBuffered_2(b *testing.B) { 30 | for i := 0; i < b.N; i++ { 31 | PipelineFan(2) 32 | } 33 | } 34 | 35 | func BenchmarkPipelineFanBuffered_3(b *testing.B) { 36 | for i := 0; i < b.N; i++ { 37 | PipelineFan(3) 38 | } 39 | } 40 | 41 | func BenchmarkPipelineFanBuffered_4(b *testing.B) { 42 | for i := 0; i < b.N; i++ { 43 | PipelineFan(4) 44 | } 45 | } 46 | 47 | func BenchmarkPipelineFanBuffered_5(b *testing.B) { 48 | for i := 0; i < b.N; i++ { 49 | PipelineFan(5) 50 | } 51 | } 52 | 53 | func BenchmarkPipelineFanBuffered_6(b *testing.B) { 54 | for i := 0; i < b.N; i++ { 55 | PipelineFan(6) 56 | } 57 | } 58 | 59 | func BenchmarkPipelineFanBuffered_7(b *testing.B) { 60 | for i := 0; i < b.N; i++ { 61 | PipelineFan(7) 62 | } 63 | } 64 | 65 | func BenchmarkPipelineFanBuffered_8(b *testing.B) { 66 | for i := 0; i < b.N; i++ { 67 | PipelineFan(8) 68 | } 69 | } 70 | 71 | func BenchmarkPipelineFanBuffered_9(b *testing.B) { 72 | for i := 0; i < b.N; i++ { 73 | PipelineFan(9) 74 | } 75 | } 76 | 77 | func BenchmarkPipelineFanBuffered_n10(b *testing.B) { 78 | for i := 0; i < b.N; i++ { 79 | PipelineFan(10) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /buffer_size/result.txt: -------------------------------------------------------------------------------- 1 | BenchmarkPipelineFanBuffered_0 2 | goos: darwin 3 | goarch: amd64 4 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 5 | BenchmarkPipelineFanBuffered_0-8 1 9219641894 ns/op 6 | PASS 7 | ok github.com/shitaibin/golang_pipeline_step_by_step 9.226s 8 | 9 | BenchmarkPipelineFanBuffered_1 10 | goos: darwin 11 | goarch: amd64 12 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 13 | BenchmarkPipelineFanBuffered_1-8 1 8776362944 ns/op 14 | PASS 15 | ok github.com/shitaibin/golang_pipeline_step_by_step 8.783s 16 | 17 | BenchmarkPipelineFanBuffered_2 18 | goos: darwin 19 | goarch: amd64 20 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 21 | BenchmarkPipelineFanBuffered_2-8 1 8617797676 ns/op 22 | PASS 23 | ok github.com/shitaibin/golang_pipeline_step_by_step 8.625s 24 | 25 | BenchmarkPipelineFanBuffered_3 26 | goos: darwin 27 | goarch: amd64 28 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 29 | BenchmarkPipelineFanBuffered_3-8 1 8474600329 ns/op 30 | PASS 31 | ok github.com/shitaibin/golang_pipeline_step_by_step 8.481s 32 | 33 | BenchmarkPipelineFanBuffered_4 34 | goos: darwin 35 | goarch: amd64 36 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 37 | BenchmarkPipelineFanBuffered_4-8 1 7644141054 ns/op 38 | PASS 39 | ok github.com/shitaibin/golang_pipeline_step_by_step 7.650s 40 | 41 | BenchmarkPipelineFanBuffered_5 42 | goos: darwin 43 | goarch: amd64 44 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 45 | BenchmarkPipelineFanBuffered_5-8 1 7517122406 ns/op 46 | PASS 47 | ok github.com/shitaibin/golang_pipeline_step_by_step 7.523s 48 | 49 | BenchmarkPipelineFanBuffered_6 50 | goos: darwin 51 | goarch: amd64 52 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 53 | BenchmarkPipelineFanBuffered_6-8 1 7495494588 ns/op 54 | PASS 55 | ok github.com/shitaibin/golang_pipeline_step_by_step 7.501s 56 | 57 | BenchmarkPipelineFanBuffered_7 58 | goos: darwin 59 | goarch: amd64 60 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 61 | BenchmarkPipelineFanBuffered_7-8 1 7359642386 ns/op 62 | PASS 63 | ok github.com/shitaibin/golang_pipeline_step_by_step 7.366s 64 | 65 | BenchmarkPipelineFanBuffered_8 66 | goos: darwin 67 | goarch: amd64 68 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 69 | BenchmarkPipelineFanBuffered_8-8 1 7250329258 ns/op 70 | PASS 71 | ok github.com/shitaibin/golang_pipeline_step_by_step 7.256s 72 | 73 | BenchmarkPipelineFanBuffered_9 74 | goos: darwin 75 | goarch: amd64 76 | pkg: github.com/shitaibin/golang_pipeline_step_by_step 77 | BenchmarkPipelineFanBuffered_9-8 1 7242636881 ns/op 78 | PASS 79 | ok github.com/shitaibin/golang_pipeline_step_by_step 7.248s 80 | -------------------------------------------------------------------------------- /buffer_size/run.sh: -------------------------------------------------------------------------------- 1 | n=0 2 | while(($n<10)) 3 | do 4 | name="BenchmarkPipelineFanBuffered_"$n 5 | echo $name 6 | go test -test.bench=$name 7 | echo "" 8 | let "n++" 9 | done -------------------------------------------------------------------------------- /fan_model_slow/hi_fan.go: -------------------------------------------------------------------------------- 1 | // hi_fan.go 2 | package main 3 | 4 | import ( 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | } 27 | }() 28 | 29 | return out 30 | } 31 | 32 | func merge(cs ...<-chan int) <-chan int { 33 | out := make(chan int) 34 | 35 | var wg sync.WaitGroup 36 | 37 | collect := func(in <-chan int) { 38 | defer wg.Done() 39 | for n := range in { 40 | out <- n 41 | } 42 | } 43 | 44 | wg.Add(len(cs)) 45 | // FAN-IN 46 | for _, c := range cs { 47 | go collect(c) 48 | } 49 | 50 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 51 | // wg.Wait() 52 | // close(out) 53 | 54 | // 正确方式 55 | go func() { 56 | wg.Wait() 57 | close(out) 58 | }() 59 | 60 | return out 61 | } 62 | 63 | func main() { 64 | in := producer(10000000) 65 | 66 | // FAN-OUT 67 | c1 := square(in) 68 | c2 := square(in) 69 | c3 := square(in) 70 | 71 | // consumer 72 | for _ = range merge(c1, c2, c3) { 73 | } 74 | } -------------------------------------------------------------------------------- /fan_model_slow/hi_fan_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func producer(nums ...int) <-chan int { 9 | out := make(chan int) 10 | go func() { 11 | defer close(out) 12 | for _, n := range nums { 13 | out <- i 14 | } 15 | }() 16 | return out 17 | } 18 | 19 | func square(inCh <-chan int) <-chan int { 20 | out := make(chan int) 21 | go func() { 22 | defer close(out) 23 | for n := range inCh { 24 | out <- n * n 25 | } 26 | }() 27 | 28 | return out 29 | } 30 | 31 | func merge(cs ...<-chan int) <-chan int { 32 | out := make(chan int) 33 | 34 | var wg sync.WaitGroup 35 | 36 | collect := func(in <-chan int) { 37 | defer wg.Done() 38 | for n := range in { 39 | out <- n 40 | } 41 | } 42 | 43 | wg.Add(len(cs)) 44 | // FAN-IN 45 | for _, c := range cs { 46 | go collect(c) 47 | } 48 | 49 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 50 | // wg.Wait() 51 | // close(out) 52 | 53 | // 正确方式 54 | go func() { 55 | wg.Wait() 56 | close(out) 57 | }() 58 | 59 | return out 60 | } 61 | 62 | func main() { 63 | in := producer(1, 2, 3, 4) 64 | 65 | // FAN-OUT 66 | c1 := square(in) 67 | c2 := square(in) 68 | c3 := square(in) 69 | 70 | // consumer 71 | for ret := range merge(c1, c2, c3) { 72 | fmt.Printf("%3d ", ret) 73 | } 74 | fmt.Println() 75 | } -------------------------------------------------------------------------------- /fan_model_slow/hi_simple.go: -------------------------------------------------------------------------------- 1 | // hi_simple.go 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | } 27 | }() 28 | 29 | return out 30 | } 31 | 32 | func main() { 33 | in := producer(10000000) 34 | ch := square(in) 35 | 36 | // consumer 37 | for _ = range ch { 38 | } 39 | } -------------------------------------------------------------------------------- /hi_fan.go: -------------------------------------------------------------------------------- 1 | // hi_fan.go 2 | package main 3 | 4 | import ( 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | // simulate 27 | time.Sleep(time.Second) 28 | } 29 | }() 30 | 31 | return out 32 | } 33 | 34 | func merge(cs ...<-chan int) <-chan int { 35 | out := make(chan int) 36 | 37 | var wg sync.WaitGroup 38 | 39 | collect := func(in <-chan int) { 40 | defer wg.Done() 41 | for n := range in { 42 | out <- n 43 | } 44 | } 45 | 46 | wg.Add(len(cs)) 47 | // FAN-IN 48 | for _, c := range cs { 49 | go collect(c) 50 | } 51 | 52 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 53 | // wg.Wait() 54 | // close(out) 55 | 56 | // 正确方式 57 | go func() { 58 | wg.Wait() 59 | close(out) 60 | }() 61 | 62 | return out 63 | } 64 | 65 | func main() { 66 | in := producer(10) 67 | 68 | // FAN-OUT 69 | c1 := square(in) 70 | c2 := square(in) 71 | c3 := square(in) 72 | 73 | // consumer 74 | for _ = range merge(c1, c2, c3) { 75 | } 76 | } -------------------------------------------------------------------------------- /hi_fan_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func producer(nums ...int) <-chan int { 9 | out := make(chan int) 10 | go func() { 11 | defer close(out) 12 | for _, n := range nums { 13 | out <- i 14 | } 15 | }() 16 | return out 17 | } 18 | 19 | func square(inCh <-chan int) <-chan int { 20 | out := make(chan int) 21 | go func() { 22 | defer close(out) 23 | for n := range inCh { 24 | out <- n * n 25 | } 26 | }() 27 | 28 | return out 29 | } 30 | 31 | func merge(cs ...<-chan int) <-chan int { 32 | out := make(chan int) 33 | 34 | var wg sync.WaitGroup 35 | 36 | collect := func(in <-chan int) { 37 | defer wg.Done() 38 | for n := range in { 39 | out <- n 40 | } 41 | } 42 | 43 | wg.Add(len(cs)) 44 | // FAN-IN 45 | for _, c := range cs { 46 | go collect(c) 47 | } 48 | 49 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 50 | // wg.Wait() 51 | // close(out) 52 | 53 | // 正确方式 54 | go func() { 55 | wg.Wait() 56 | close(out) 57 | }() 58 | 59 | return out 60 | } 61 | 62 | func main() { 63 | in := producer(1, 2, 3, 4) 64 | 65 | // FAN-OUT 66 | c1 := square(in) 67 | c2 := square(in) 68 | c3 := square(in) 69 | 70 | // consumer 71 | for ret := range merge(c1, c2, c3) { 72 | fmt.Printf("%3d ", ret) 73 | } 74 | fmt.Println() 75 | } -------------------------------------------------------------------------------- /hi_simple.go: -------------------------------------------------------------------------------- 1 | // hi_simple.go 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | // simulate 27 | time.Sleep(time.Second) 28 | } 29 | }() 30 | 31 | return out 32 | } 33 | 34 | func main() { 35 | in := producer(10) 36 | ch := square(in) 37 | 38 | // consumer 39 | for _ = range ch { 40 | } 41 | } -------------------------------------------------------------------------------- /master/hi_fan.go: -------------------------------------------------------------------------------- 1 | // hi_fan.go 2 | package main 3 | 4 | import ( 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | // simulate 27 | time.Sleep(time.Second) 28 | } 29 | }() 30 | 31 | return out 32 | } 33 | 34 | func merge(cs ...<-chan int) <-chan int { 35 | out := make(chan int) 36 | 37 | var wg sync.WaitGroup 38 | 39 | collect := func(in <-chan int) { 40 | defer wg.Done() 41 | for n := range in { 42 | out <- n 43 | } 44 | } 45 | 46 | wg.Add(len(cs)) 47 | // FAN-IN 48 | for _, c := range cs { 49 | go collect(c) 50 | } 51 | 52 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 53 | // wg.Wait() 54 | // close(out) 55 | 56 | // 正确方式 57 | go func() { 58 | wg.Wait() 59 | close(out) 60 | }() 61 | 62 | return out 63 | } 64 | 65 | func main() { 66 | in := producer(10) 67 | 68 | // FAN-OUT 69 | c1 := square(in) 70 | c2 := square(in) 71 | c3 := square(in) 72 | 73 | // consumer 74 | for _ = range merge(c1, c2, c3) { 75 | } 76 | } -------------------------------------------------------------------------------- /master/hi_fan_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func producer(nums ...int) <-chan int { 9 | out := make(chan int) 10 | go func() { 11 | defer close(out) 12 | for _, n := range nums { 13 | out <- i 14 | } 15 | }() 16 | return out 17 | } 18 | 19 | func square(inCh <-chan int) <-chan int { 20 | out := make(chan int) 21 | go func() { 22 | defer close(out) 23 | for n := range inCh { 24 | out <- n * n 25 | } 26 | }() 27 | 28 | return out 29 | } 30 | 31 | func merge(cs ...<-chan int) <-chan int { 32 | out := make(chan int) 33 | 34 | var wg sync.WaitGroup 35 | 36 | collect := func(in <-chan int) { 37 | defer wg.Done() 38 | for n := range in { 39 | out <- n 40 | } 41 | } 42 | 43 | wg.Add(len(cs)) 44 | // FAN-IN 45 | for _, c := range cs { 46 | go collect(c) 47 | } 48 | 49 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 50 | // wg.Wait() 51 | // close(out) 52 | 53 | // 正确方式 54 | go func() { 55 | wg.Wait() 56 | close(out) 57 | }() 58 | 59 | return out 60 | } 61 | 62 | func main() { 63 | in := producer(1, 2, 3, 4) 64 | 65 | // FAN-OUT 66 | c1 := square(in) 67 | c2 := square(in) 68 | c3 := square(in) 69 | 70 | // consumer 71 | for ret := range merge(c1, c2, c3) { 72 | fmt.Printf("%3d ", ret) 73 | } 74 | fmt.Println() 75 | } -------------------------------------------------------------------------------- /master/hi_simple.go: -------------------------------------------------------------------------------- 1 | // hi_simple.go 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | // simulate 27 | time.Sleep(time.Second) 28 | } 29 | }() 30 | 31 | return out 32 | } 33 | 34 | func main() { 35 | in := producer(10) 36 | ch := square(in) 37 | 38 | // consumer 39 | for _ = range ch { 40 | } 41 | } -------------------------------------------------------------------------------- /optimize_fan_model/hi_fan.go: -------------------------------------------------------------------------------- 1 | // hi_fan.go 2 | package main 3 | 4 | import ( 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | } 27 | }() 28 | 29 | return out 30 | } 31 | 32 | func merge(cs ...<-chan int) <-chan int { 33 | out := make(chan int) 34 | 35 | var wg sync.WaitGroup 36 | 37 | collect := func(in <-chan int) { 38 | defer wg.Done() 39 | for n := range in { 40 | out <- n 41 | } 42 | } 43 | 44 | wg.Add(len(cs)) 45 | // FAN-IN 46 | for _, c := range cs { 47 | go collect(c) 48 | } 49 | 50 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 51 | // wg.Wait() 52 | // close(out) 53 | 54 | // 正确方式 55 | go func() { 56 | wg.Wait() 57 | close(out) 58 | }() 59 | 60 | return out 61 | } 62 | 63 | func main() { 64 | in := producer(10000000) 65 | 66 | // FAN-OUT 67 | c1 := square(in) 68 | c2 := square(in) 69 | c3 := square(in) 70 | 71 | // consumer 72 | for _ = range merge(c1, c2, c3) { 73 | } 74 | } -------------------------------------------------------------------------------- /optimize_fan_model/hi_fan_buffered.go: -------------------------------------------------------------------------------- 1 | // hi_fan.go 2 | package main 3 | 4 | import ( 5 | "sync" 6 | "time" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | } 27 | }() 28 | 29 | return out 30 | } 31 | 32 | func merge(cs ...<-chan int) <-chan int { 33 | out := make(chan int, 100) 34 | 35 | var wg sync.WaitGroup 36 | 37 | collect := func(in <-chan int) { 38 | defer wg.Done() 39 | for n := range in { 40 | out <- n 41 | } 42 | } 43 | 44 | wg.Add(len(cs)) 45 | // FAN-IN 46 | for _, c := range cs { 47 | go collect(c) 48 | } 49 | 50 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 51 | // wg.Wait() 52 | // close(out) 53 | 54 | // 正确方式 55 | go func() { 56 | wg.Wait() 57 | close(out) 58 | }() 59 | 60 | return out 61 | } 62 | 63 | func main() { 64 | in := producer(10000000) 65 | 66 | // FAN-OUT 67 | c1 := square(in) 68 | c2 := square(in) 69 | c3 := square(in) 70 | 71 | // consumer 72 | for _ = range merge(c1, c2, c3) { 73 | } 74 | } -------------------------------------------------------------------------------- /optimize_fan_model/hi_fan_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func producer(nums ...int) <-chan int { 9 | out := make(chan int) 10 | go func() { 11 | defer close(out) 12 | for _, n := range nums { 13 | out <- i 14 | } 15 | }() 16 | return out 17 | } 18 | 19 | func square(inCh <-chan int) <-chan int { 20 | out := make(chan int) 21 | go func() { 22 | defer close(out) 23 | for n := range inCh { 24 | out <- n * n 25 | } 26 | }() 27 | 28 | return out 29 | } 30 | 31 | func merge(cs ...<-chan int) <-chan int { 32 | out := make(chan int) 33 | 34 | var wg sync.WaitGroup 35 | 36 | collect := func(in <-chan int) { 37 | defer wg.Done() 38 | for n := range in { 39 | out <- n 40 | } 41 | } 42 | 43 | wg.Add(len(cs)) 44 | // FAN-IN 45 | for _, c := range cs { 46 | go collect(c) 47 | } 48 | 49 | // 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读 50 | // wg.Wait() 51 | // close(out) 52 | 53 | // 正确方式 54 | go func() { 55 | wg.Wait() 56 | close(out) 57 | }() 58 | 59 | return out 60 | } 61 | 62 | func main() { 63 | in := producer(1, 2, 3, 4) 64 | 65 | // FAN-OUT 66 | c1 := square(in) 67 | c2 := square(in) 68 | c3 := square(in) 69 | 70 | // consumer 71 | for ret := range merge(c1, c2, c3) { 72 | fmt.Printf("%3d ", ret) 73 | } 74 | fmt.Println() 75 | } -------------------------------------------------------------------------------- /optimize_fan_model/hi_simple.go: -------------------------------------------------------------------------------- 1 | // hi_simple.go 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func producer(n int) <-chan int { 10 | out := make(chan int) 11 | go func() { 12 | defer close(out) 13 | for i := 0; i < n; i++ { 14 | out <- i 15 | } 16 | }() 17 | return out 18 | } 19 | 20 | func square(inCh <-chan int) <-chan int { 21 | out := make(chan int) 22 | go func() { 23 | defer close(out) 24 | for n := range inCh { 25 | out <- n * n 26 | } 27 | }() 28 | 29 | return out 30 | } 31 | 32 | func main() { 33 | in := producer(10000000) 34 | ch := square(in) 35 | 36 | // consumer 37 | for _ = range ch { 38 | } 39 | } --------------------------------------------------------------------------------