├── LICENSE ├── README.md ├── README_ZH.md ├── ants.go ├── ants_bench_1000w.png ├── ants_bench_100w.png ├── ants_bench_10w.png ├── ants_bench_poolwithfunc.png ├── ants_benchmark_test.go ├── ants_benchmarks.png ├── ants_logo.png ├── ants_test.go ├── benchmark_pool.png ├── examples └── main.go ├── pool.go ├── pool_func.go ├── worker.go └── worker_func.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Andy Pan 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 | # ants 2 | 3 |
4 | 5 |

A goroutine pool for Go

6 | 7 | 8 | 9 | [![godoc for panjf2000/ants][1]][2] 10 | [![goreportcard for panjf2000/ants][3]][4] 11 | [![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php) 12 | 13 | [中文项目说明](README_ZH.md) | [Project Tutorial](http://blog.taohuawu.club/article/42) 14 | 15 | Package ants implements a fixed goroutine pool for managing and recycling a massive number of goroutines, allowing developers to limit the number of goroutines that created by your concurrent programs. 16 | 17 | ## Features: 18 | 19 | - Automatically managing and recycling a massive number of goroutines. 20 | - Friendly interfaces: submitting tasks, getting the number of running goroutines, readjusting capacity of pool dynamically, closing pool. 21 | - Efficient in memory usage and it even achieves higher performance than unlimited goroutines in golang. 22 | 23 | 24 | ## How to install 25 | 26 | ``` sh 27 | go get -u github.com/panjf2000/ants 28 | ``` 29 | 30 | Or, using glide: 31 | 32 | ``` sh 33 | glide get github.com/panjf2000/ants 34 | ``` 35 | 36 | ## How to use 37 | If your program will generate a massive number of goroutines and you don't want them to consume a vast amount of memory, with ants, all you need to do is to import ants package and submit all your tasks to the default limited pool created when ants was imported: 38 | 39 | ``` go 40 | 41 | package main 42 | 43 | import ( 44 | "fmt" 45 | "sync" 46 | "sync/atomic" 47 | 48 | "github.com/panjf2000/ants" 49 | "time" 50 | ) 51 | 52 | var sum int32 53 | 54 | func myFunc(i interface{}) error { 55 | n := i.(int) 56 | atomic.AddInt32(&sum, int32(n)) 57 | fmt.Printf("run with %d\n", n) 58 | return nil 59 | } 60 | 61 | func demoFunc() error { 62 | time.Sleep(10 * time.Millisecond) 63 | fmt.Println("Hello World!") 64 | return nil 65 | } 66 | 67 | func main() { 68 | runTimes := 1000 69 | 70 | // use the common pool 71 | var wg sync.WaitGroup 72 | for i := 0; i < runTimes; i++ { 73 | wg.Add(1) 74 | ants.Submit(func() error { 75 | demoFunc() 76 | wg.Done() 77 | return nil 78 | }) 79 | } 80 | wg.Wait() 81 | fmt.Printf("running goroutines: %d\n", ants.Running()) 82 | fmt.Printf("finish all tasks.\n") 83 | 84 | // use the pool with a function 85 | // set 10 the size of goroutine pool 86 | p, _ := ants.NewPoolWithFunc(10, func(i interface{}) error { 87 | myFunc(i) 88 | wg.Done() 89 | return nil 90 | }) 91 | // submit tasks 92 | for i := 0; i < runTimes; i++ { 93 | wg.Add(1) 94 | p.Serve(i) 95 | } 96 | wg.Wait() 97 | fmt.Printf("running goroutines: %d\n", p.Running()) 98 | fmt.Printf("finish all tasks, result is %d\n", sum) 99 | } 100 | ``` 101 | 102 | ## Submit tasks 103 | Tasks can be submitted by calling `ants.Submit(func())` 104 | ```go 105 | ants.Submit(func() {}) 106 | ``` 107 | 108 | ## Custom limited pool 109 | Ants also supports custom limited pool. You can use the `NewPool` method to create a pool with the given capacity, as following: 110 | 111 | ``` go 112 | // set 10000 the size of goroutine pool 113 | p, _ := ants.NewPool(10000) 114 | // submit a task 115 | p.Submit(func() {}) 116 | ``` 117 | 118 | ## Readjusting pool capacity 119 | You can change ants pool capacity at any time with `ReSize(int)`: 120 | 121 | ``` go 122 | pool.ReSize(1000) // Readjust its capacity to 1000 123 | pool.ReSize(100000) // Readjust its capacity to 100000 124 | ``` 125 | 126 | Don't worry about the synchronous problems in this case, this method is thread-safe. 127 | 128 | ## About sequence 129 | All the tasks submitted to ants pool will not be guaranteed to be processed in order, because those tasks distribute among a series of concurrent workers, thus those tasks are processed concurrently. 130 | 131 | ## Benchmarks 132 |
133 | 134 | In that benchmark-picture, the first and second benchmarks performed test with 100w tasks and the rest of benchmarks performed test with 1000w tasks, both unlimited goroutines and ants pool, and the capacity of this ants goroutine-pool was limited to 5w. 135 | 136 | - BenchmarkGoroutine-4 represent the benchmarks with unlimited goroutines in golang. 137 | 138 | - BenchmarkPoolGroutine-4 represent the benchmarks with a ants pool. 139 | 140 | The test data above is a basic benchmark and the more detailed benchmarks will be uploaded later. 141 | 142 | ### Benchmarks with Pool 143 | 144 | ![](benchmark_pool.png) 145 | 146 | 147 | 148 | ### Benchmarks with PoolWithFunc 149 | 150 | ![](ants_bench_poolwithfunc.png) 151 | 152 | ### Throughput 153 | 154 | #### 10w tasks 155 | 156 | ![](ants_bench_10w.png) 157 | 158 | #### 100w tasks 159 | 160 | ![](ants_bench_100w.png) 161 | 162 | #### 1000W tasks 163 | 164 | ![](ants_bench_1000w.png) 165 | 166 | There was only the test of `ants` Pool because my computer was crash when it reached 1000w goroutines. 167 | 168 | [1]: https://godoc.org/github.com/panjf2000/ants?status.svg 169 | [2]: https://godoc.org/github.com/panjf2000/ants 170 | [3]: https://goreportcard.com/badge/github.com/panjf2000/ants 171 | [4]: https://goreportcard.com/report/github.com/panjf2000/ants 172 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # ants 2 | 3 |
4 | 5 |

A goroutine pool for Go

6 | 7 | 8 | 9 | [![godoc for panjf2000/ants][1]][2] [![goreportcard for panjf2000/ants][3]][4] [![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php) 10 | 11 | [英文说明页](README.md) | [项目介绍文章传送门](http://blog.taohuawu.club/article/42) 12 | 13 | `ants`是一个高性能的协程池,实现了对大规模goroutine的调度管理、goroutine复用,允许使用者在开发并发程序的时候限制协程数量,复用资源,达到更高效执行任务的效果。 14 | 15 | ## 功能: 16 | 17 | - 实现了自动调度并发的goroutine,复用goroutine 18 | - 提供了友好的接口:任务提交、获取运行中的协程数量、动态调整协程池大小 19 | - 资源复用,极大节省内存使用量;在大规模批量并发任务场景下比原生goroutine并发具有更高的性能 20 | 21 | 22 | ## 安装 23 | 24 | ``` sh 25 | go get -u github.com/panjf2000/ants 26 | ``` 27 | 28 | 使用包管理工具 glide 安装: 29 | 30 | ``` sh 31 | glide get github.com/panjf2000/ants 32 | ``` 33 | 34 | ## 使用 35 | 写 go 并发程序的时候如果程序会启动大量的 goroutine ,势必会消耗大量的系统资源(内存,CPU),通过使用 `ants`,可以实例化一个协程池,复用 goroutine ,节省资源,提升性能: 36 | 37 | ``` go 38 | 39 | package main 40 | 41 | import ( 42 | "fmt" 43 | "sync" 44 | "sync/atomic" 45 | 46 | "github.com/panjf2000/ants" 47 | "time" 48 | ) 49 | 50 | var sum int32 51 | 52 | func myFunc(i interface{}) error { 53 | n := i.(int) 54 | atomic.AddInt32(&sum, int32(n)) 55 | fmt.Printf("run with %d\n", n) 56 | return nil 57 | } 58 | 59 | func demoFunc() error { 60 | time.Sleep(10 * time.Millisecond) 61 | fmt.Println("Hello World!") 62 | return nil 63 | } 64 | 65 | func main() { 66 | runTimes := 1000 67 | 68 | // use the common pool 69 | var wg sync.WaitGroup 70 | for i := 0; i < runTimes; i++ { 71 | wg.Add(1) 72 | ants.Submit(func() error { 73 | demoFunc() 74 | wg.Done() 75 | return nil 76 | }) 77 | } 78 | wg.Wait() 79 | fmt.Printf("running goroutines: %d\n", ants.Running()) 80 | fmt.Printf("finish all tasks.\n") 81 | 82 | // use the pool with a function 83 | // set 10 the size of goroutine pool 84 | p, _ := ants.NewPoolWithFunc(10, func(i interface{}) error { 85 | myFunc(i) 86 | wg.Done() 87 | return nil 88 | }) 89 | // submit tasks 90 | for i := 0; i < runTimes; i++ { 91 | wg.Add(1) 92 | p.Serve(i) 93 | } 94 | wg.Wait() 95 | fmt.Printf("running goroutines: %d\n", p.Running()) 96 | fmt.Printf("finish all tasks, result is %d\n", sum) 97 | } 98 | ``` 99 | 100 | ## 任务提交 101 | 提交任务通过调用 `ants.Submit(func())`方法: 102 | ```go 103 | ants.Submit(func() {}) 104 | ``` 105 | 106 | ## 自定义池 107 | `ants`支持实例化使用者自己的一个 Pool ,指定具体的池容量;通过调用 `NewPool` 方法可以实例化一个新的带有指定容量的 Pool ,如下: 108 | 109 | ``` go 110 | // set 10000 the size of goroutine pool 111 | p, _ := ants.NewPool(10000) 112 | // submit a task 113 | p.Submit(func() {}) 114 | ``` 115 | 116 | ## 动态调整协程池容量 117 | 需要动态调整协程池容量可以通过调用`ReSize(int)`: 118 | 119 | ``` go 120 | pool.ReSize(1000) // Readjust its capacity to 1000 121 | pool.ReSize(100000) // Readjust its capacity to 100000 122 | ``` 123 | 124 | 该方法是线程安全的。 125 | 126 | ## Benchmarks 127 | 128 | 系统参数: 129 | 130 | ``` 131 | OS : macOS High Sierra 132 | Processor : 2.7 GHz Intel Core i5 133 | Memory : 8 GB 1867 MHz DDR3 134 | ``` 135 | 136 | 137 | 138 |
139 | 140 | 上图中的前两个 benchmark 测试结果是基于100w任务量的条件,剩下的几个是基于1000w任务量的测试结果,`ants`的默认池容量是5w。 141 | 142 | - BenchmarkGoroutine-4 代表原生goroutine 143 | 144 | - BenchmarkPoolGroutine-4 代表使用协程池`ants` 145 | 146 | ### Benchmarks with Pool 147 | 148 | ![](benchmark_pool.png) 149 | 150 | 151 | 152 | ### Benchmarks with PoolWithFunc 153 | 154 | ![](ants_bench_poolwithfunc.png) 155 | 156 | ### 吞吐量测试 157 | 158 | #### 10w 任务量 159 | 160 | ![](ants_bench_10w.png) 161 | 162 | #### 100w 任务量 163 | 164 | ![](ants_bench_100w.png) 165 | 166 | #### 1000w 任务量 167 | 168 | ![](ants_bench_1000w.png) 169 | 170 | 1000w任务量的场景下,我的电脑已经无法支撑 golang 的原生 goroutine 并发,所以只测出了使用`ants`池的测试结果。 171 | 172 | [1]: https://godoc.org/github.com/panjf2000/ants?status.svg 173 | [2]: https://godoc.org/github.com/panjf2000/ants 174 | [3]: https://goreportcard.com/badge/github.com/panjf2000/ants 175 | [4]: https://goreportcard.com/report/github.com/panjf2000/ants 176 | -------------------------------------------------------------------------------- /ants.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants 24 | 25 | import ( 26 | "errors" 27 | "math" 28 | "runtime" 29 | ) 30 | 31 | const ( 32 | // DefaultPoolSize is the default capacity for a default goroutine pool 33 | DefaultPoolSize = math.MaxInt32 34 | 35 | // DefaultCleanIntervalTime is the interval time to clean up goroutines 36 | DefaultCleanIntervalTime = 30 37 | ) 38 | 39 | // Init a instance pool when importing ants 40 | var defaultPool, _ = NewPool(DefaultPoolSize) 41 | 42 | // Submit submit a task to pool 43 | func Submit(task f) error { 44 | return defaultPool.Submit(task) 45 | } 46 | 47 | // Running returns the number of the currently running goroutines 48 | func Running() int { 49 | return defaultPool.Running() 50 | } 51 | 52 | // Cap returns the capacity of this default pool 53 | func Cap() int { 54 | return defaultPool.Cap() 55 | } 56 | 57 | // Free returns the available goroutines to work 58 | func Free() int { 59 | return defaultPool.Free() 60 | } 61 | 62 | // Release Closed the default pool 63 | func Release() { 64 | defaultPool.Release() 65 | } 66 | 67 | // Errors for the Ants API 68 | var ( 69 | ErrPoolSizeInvalid = errors.New("invalid size for pool") 70 | ErrPoolClosed = errors.New("this pool has been closed") 71 | ) 72 | 73 | var workerArgsCap = func() int { 74 | if runtime.GOMAXPROCS(0) == 1 { 75 | return 0 76 | } 77 | return 1 78 | }() 79 | -------------------------------------------------------------------------------- /ants_bench_1000w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/ants_bench_1000w.png -------------------------------------------------------------------------------- /ants_bench_100w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/ants_bench_100w.png -------------------------------------------------------------------------------- /ants_bench_10w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/ants_bench_10w.png -------------------------------------------------------------------------------- /ants_bench_poolwithfunc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/ants_bench_poolwithfunc.png -------------------------------------------------------------------------------- /ants_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants_test 24 | 25 | import ( 26 | "sync" 27 | "testing" 28 | "time" 29 | 30 | "github.com/panjf2000/ants" 31 | ) 32 | 33 | const ( 34 | _ = 1 << (10 * iota) 35 | KiB // 1024 36 | MiB // 1048576 37 | GiB // 1073741824 38 | TiB // 1099511627776 (超过了int32的范围) 39 | PiB // 1125899906842624 40 | EiB // 1152921504606846976 41 | ZiB // 1180591620717411303424 (超过了int64的范围) 42 | YiB // 1208925819614629174706176 43 | ) 44 | const RunTimes = 1000000 45 | const loop = 10 46 | 47 | func demoFunc() error { 48 | n := 10 49 | time.Sleep(time.Duration(n) * time.Millisecond) 50 | return nil 51 | } 52 | 53 | func demoPoolFunc(args interface{}) error { 54 | //m := args.(int) 55 | //var n int 56 | //for i := 0; i < m; i++ { 57 | // n += i 58 | //} 59 | //return nil 60 | n := args.(int) 61 | time.Sleep(time.Duration(n) * time.Millisecond) 62 | return nil 63 | } 64 | 65 | func BenchmarkGoroutineWithFunc(b *testing.B) { 66 | var wg sync.WaitGroup 67 | for i := 0; i < b.N; i++ { 68 | for j := 0; j < RunTimes; j++ { 69 | wg.Add(1) 70 | go func() { 71 | demoPoolFunc(loop) 72 | wg.Done() 73 | }() 74 | } 75 | wg.Wait() 76 | } 77 | } 78 | 79 | func BenchmarkAntsPoolWithFunc(b *testing.B) { 80 | var wg sync.WaitGroup 81 | p, _ := ants.NewPoolWithFunc(50000, func(i interface{}) error { 82 | demoPoolFunc(i) 83 | wg.Done() 84 | return nil 85 | }) 86 | for i := 0; i < b.N; i++ { 87 | for j := 0; j < RunTimes; j++ { 88 | wg.Add(1) 89 | p.Serve(loop) 90 | } 91 | wg.Wait() 92 | b.Logf("running goroutines: %d", p.Running()) 93 | } 94 | } 95 | 96 | func BenchmarkGoroutine(b *testing.B) { 97 | for i := 0; i < b.N; i++ { 98 | for j := 0; j < RunTimes; j++ { 99 | go demoPoolFunc(loop) 100 | } 101 | } 102 | } 103 | 104 | func BenchmarkAntsPool(b *testing.B) { 105 | p, _ := ants.NewPoolWithFunc(50000, demoPoolFunc) 106 | b.ResetTimer() 107 | for i := 0; i < b.N; i++ { 108 | for j := 0; j < RunTimes; j++ { 109 | p.Serve(loop) 110 | } 111 | // b.Logf("running goroutines: %d", p.Running()) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /ants_benchmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/ants_benchmarks.png -------------------------------------------------------------------------------- /ants_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/ants_logo.png -------------------------------------------------------------------------------- /ants_test.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants_test 24 | 25 | import ( 26 | "runtime" 27 | "sync" 28 | "testing" 29 | 30 | "github.com/panjf2000/ants" 31 | ) 32 | 33 | var n = 1000000 34 | 35 | func TestDefaultPool(t *testing.T) { 36 | var wg sync.WaitGroup 37 | for i := 0; i < n; i++ { 38 | wg.Add(1) 39 | ants.Submit(func() error { 40 | demoFunc() 41 | wg.Done() 42 | return nil 43 | }) 44 | } 45 | wg.Wait() 46 | 47 | //t.Logf("pool capacity:%d", ants.Cap()) 48 | //t.Logf("free workers number:%d", ants.Free()) 49 | 50 | t.Logf("running workers number:%d", ants.Running()) 51 | mem := runtime.MemStats{} 52 | runtime.ReadMemStats(&mem) 53 | t.Logf("memory usage:%d MB", mem.TotalAlloc/MiB) 54 | } 55 | 56 | func TestNoPool(t *testing.T) { 57 | var wg sync.WaitGroup 58 | for i := 0; i < n; i++ { 59 | wg.Add(1) 60 | go func() { 61 | demoFunc() 62 | wg.Done() 63 | }() 64 | } 65 | 66 | wg.Wait() 67 | mem := runtime.MemStats{} 68 | runtime.ReadMemStats(&mem) 69 | t.Logf("memory usage:%d MB", mem.TotalAlloc/MiB) 70 | } 71 | 72 | // func TestAntsPoolWithFunc(t *testing.T) { 73 | // var wg sync.WaitGroup 74 | // p, _ := ants.NewPoolWithFunc(50000, func(i interface{}) error { 75 | // demoPoolFunc(i) 76 | // wg.Done() 77 | // return nil 78 | // }) 79 | // for i := 0; i < n; i++ { 80 | // wg.Add(1) 81 | // p.Serve(n) 82 | // } 83 | // wg.Wait() 84 | 85 | // //t.Logf("pool capacity:%d", ants.Cap()) 86 | // //t.Logf("free workers number:%d", ants.Free()) 87 | 88 | // t.Logf("running workers number:%d", p.Running()) 89 | // mem := runtime.MemStats{} 90 | // runtime.ReadMemStats(&mem) 91 | // t.Logf("memory usage:%d", mem.TotalAlloc/GiB) 92 | // } 93 | 94 | // func TestNoPool(t *testing.T) { 95 | // var wg sync.WaitGroup 96 | // for i := 0; i < n; i++ { 97 | // wg.Add(1) 98 | // go func() { 99 | // demoPoolFunc(n) 100 | // wg.Done() 101 | // }() 102 | // } 103 | 104 | // wg.Wait() 105 | // mem := runtime.MemStats{} 106 | // runtime.ReadMemStats(&mem) 107 | // t.Logf("memory usage:%d", mem.TotalAlloc/GiB) 108 | // } 109 | 110 | //func TestCustomPool(t *testing.T) { 111 | // p, _ := ants.NewPool(30000) 112 | // var wg sync.WaitGroup 113 | // for i := 0; i < n; i++ { 114 | // wg.Add(1) 115 | // p.Submit(func() { 116 | // demoFunc() 117 | // //demoFunc() 118 | // wg.Done() 119 | // }) 120 | // } 121 | // wg.Wait() 122 | // 123 | // //t.Logf("pool capacity:%d", p.Cap()) 124 | // //t.Logf("free workers number:%d", p.Free()) 125 | // 126 | // t.Logf("running workers number:%d", p.Running()) 127 | // mem := runtime.MemStats{} 128 | // runtime.ReadMemStats(&mem) 129 | // t.Logf("memory usage:%d", mem.TotalAlloc/1024) 130 | //} 131 | -------------------------------------------------------------------------------- /benchmark_pool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Golangltd/ants/afc0d406f8ba067d1dc28581c97c40df7e80c016/benchmark_pool.png -------------------------------------------------------------------------------- /examples/main.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package main 24 | 25 | import ( 26 | "fmt" 27 | "sync" 28 | "sync/atomic" 29 | 30 | "github.com/panjf2000/ants" 31 | "time" 32 | ) 33 | 34 | var sum int32 35 | 36 | func myFunc(i interface{}) error { 37 | n := i.(int32) 38 | atomic.AddInt32(&sum, n) 39 | fmt.Printf("run with %d\n", n) 40 | return nil 41 | } 42 | 43 | func demoFunc() error { 44 | time.Sleep(10 * time.Millisecond) 45 | fmt.Println("Hello World!") 46 | return nil 47 | } 48 | 49 | func main() { 50 | runTimes := 1000 51 | 52 | // use the common pool 53 | var wg sync.WaitGroup 54 | for i := 0; i < runTimes; i++ { 55 | wg.Add(1) 56 | ants.Submit(func() error { 57 | demoFunc() 58 | wg.Done() 59 | return nil 60 | }) 61 | } 62 | wg.Wait() 63 | fmt.Printf("running goroutines: %d\n", ants.Running()) 64 | fmt.Printf("finish all tasks.\n") 65 | 66 | // use the pool with a function 67 | // set 10 the size of goroutine pool 68 | p, _ := ants.NewPoolWithFunc(10, func(i interface{}) error { 69 | myFunc(i) 70 | wg.Done() 71 | return nil 72 | }) 73 | // submit tasks 74 | for i := 0; i < runTimes; i++ { 75 | wg.Add(1) 76 | p.Serve(int32(i)) 77 | } 78 | wg.Wait() 79 | fmt.Printf("running goroutines: %d\n", p.Running()) 80 | fmt.Printf("finish all tasks, result is %d\n", sum) 81 | } 82 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants 24 | 25 | import ( 26 | "math" 27 | "sync" 28 | "sync/atomic" 29 | ) 30 | 31 | type sig struct{} 32 | 33 | type f func() error 34 | 35 | // Pool accept the tasks from client,it limits the total 36 | // of goroutines to a given number by recycling goroutines. 37 | type Pool struct { 38 | // capacity of the pool. 39 | capacity int32 40 | 41 | // running is the number of the currently running goroutines. 42 | running int32 43 | 44 | // freeSignal is used to notice pool there are available 45 | // workers which can be sent to work. 46 | freeSignal chan sig 47 | 48 | // workers is a slice that store the available workers. 49 | workers []*Worker 50 | 51 | // release is used to notice the pool to closed itself. 52 | release chan sig 53 | 54 | // lock for synchronous operation 55 | lock sync.Mutex 56 | 57 | once sync.Once 58 | } 59 | 60 | // NewPool generates a instance of ants pool 61 | func NewPool(size int) (*Pool, error) { 62 | if size <= 0 { 63 | return nil, ErrPoolSizeInvalid 64 | } 65 | p := &Pool{ 66 | capacity: int32(size), 67 | freeSignal: make(chan sig, math.MaxInt32), 68 | release: make(chan sig, 1), 69 | } 70 | 71 | return p, nil 72 | } 73 | 74 | //------------------------------------------------------------------------- 75 | 76 | // Submit submit a task to pool 77 | func (p *Pool) Submit(task f) error { 78 | if len(p.release) > 0 { 79 | return ErrPoolClosed 80 | } 81 | w := p.getWorker() 82 | w.sendTask(task) 83 | return nil 84 | } 85 | 86 | // Running returns the number of the currently running goroutines 87 | func (p *Pool) Running() int { 88 | return int(atomic.LoadInt32(&p.running)) 89 | } 90 | 91 | // Free returns the available goroutines to work 92 | func (p *Pool) Free() int { 93 | return int(atomic.LoadInt32(&p.capacity) - atomic.LoadInt32(&p.running)) 94 | } 95 | 96 | // Cap returns the capacity of this pool 97 | func (p *Pool) Cap() int { 98 | return int(atomic.LoadInt32(&p.capacity)) 99 | } 100 | 101 | // Release Closed this pool 102 | func (p *Pool) Release() error { 103 | p.once.Do(func() { 104 | p.release <- sig{} 105 | running := p.Running() 106 | for i := 0; i < running; i++ { 107 | p.getWorker().stop() 108 | } 109 | for i := range p.workers{ 110 | p.workers[i] = nil 111 | } 112 | }) 113 | return nil 114 | } 115 | 116 | // ReSize change the capacity of this pool 117 | func (p *Pool) ReSize(size int) { 118 | if size < p.Cap() { 119 | diff := p.Cap() - size 120 | for i := 0; i < diff; i++ { 121 | p.getWorker().stop() 122 | } 123 | } else if size == p.Cap() { 124 | return 125 | } 126 | atomic.StoreInt32(&p.capacity, int32(size)) 127 | } 128 | 129 | //------------------------------------------------------------------------- 130 | 131 | // getWorker returns a available worker to run the tasks. 132 | func (p *Pool) getWorker() *Worker { 133 | var w *Worker 134 | waiting := false 135 | 136 | p.lock.Lock() 137 | workers := p.workers 138 | n := len(workers) - 1 139 | if n < 0 { 140 | if p.running >= p.capacity { 141 | waiting = true 142 | } else { 143 | p.running++ 144 | } 145 | } else { 146 | <-p.freeSignal 147 | w = workers[n] 148 | workers[n] = nil 149 | p.workers = workers[:n] 150 | } 151 | p.lock.Unlock() 152 | 153 | if waiting { 154 | <-p.freeSignal 155 | p.lock.Lock() 156 | workers = p.workers 157 | l := len(workers) - 1 158 | w = workers[l] 159 | workers[l] = nil 160 | p.workers = workers[:l] 161 | p.lock.Unlock() 162 | } else if w == nil { 163 | w = &Worker{ 164 | pool: p, 165 | task: make(chan f), 166 | } 167 | w.run() 168 | } 169 | return w 170 | } 171 | 172 | // putWorker puts a worker back into free pool, recycling the goroutines. 173 | func (p *Pool) putWorker(worker *Worker) { 174 | p.lock.Lock() 175 | p.workers = append(p.workers, worker) 176 | p.lock.Unlock() 177 | p.freeSignal <- sig{} 178 | } 179 | -------------------------------------------------------------------------------- /pool_func.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants 24 | 25 | import ( 26 | "math" 27 | "sync" 28 | "sync/atomic" 29 | ) 30 | 31 | type pf func(interface{}) error 32 | 33 | // PoolWithFunc accept the tasks from client,it limits the total 34 | // of goroutines to a given number by recycling goroutines. 35 | type PoolWithFunc struct { 36 | // capacity of the pool. 37 | capacity int32 38 | 39 | // running is the number of the currently running goroutines. 40 | running int32 41 | 42 | // freeSignal is used to notice pool there are available 43 | // workers which can be sent to work. 44 | freeSignal chan sig 45 | 46 | // workers is a slice that store the available workers. 47 | workers []*WorkerWithFunc 48 | 49 | // release is used to notice the pool to closed itself. 50 | release chan sig 51 | 52 | // lock for synchronous operation 53 | lock sync.Mutex 54 | 55 | // pf is the function for processing tasks 56 | poolFunc pf 57 | 58 | once sync.Once 59 | } 60 | 61 | // NewPoolWithFunc generates a instance of ants pool with a specific function. 62 | func NewPoolWithFunc(size int, f pf) (*PoolWithFunc, error) { 63 | if size <= 0 { 64 | return nil, ErrPoolSizeInvalid 65 | } 66 | p := &PoolWithFunc{ 67 | capacity: int32(size), 68 | freeSignal: make(chan sig, math.MaxInt32), 69 | release: make(chan sig, 1), 70 | poolFunc: f, 71 | } 72 | 73 | return p, nil 74 | } 75 | 76 | //------------------------------------------------------------------------- 77 | 78 | // Serve submit a task to pool 79 | func (p *PoolWithFunc) Serve(args interface{}) error { 80 | //if atomic.LoadInt32(&p.closed) == 1 { 81 | // return ErrPoolClosed 82 | //} 83 | if len(p.release) > 0 { 84 | return ErrPoolClosed 85 | } 86 | w := p.getWorker() 87 | w.sendTask(args) 88 | return nil 89 | } 90 | 91 | // Running returns the number of the currently running goroutines 92 | func (p *PoolWithFunc) Running() int { 93 | return int(atomic.LoadInt32(&p.running)) 94 | } 95 | 96 | // Free returns the available goroutines to work 97 | func (p *PoolWithFunc) Free() int { 98 | return int(atomic.LoadInt32(&p.capacity) - atomic.LoadInt32(&p.running)) 99 | } 100 | 101 | // Cap returns the capacity of this pool 102 | func (p *PoolWithFunc) Cap() int { 103 | return int(atomic.LoadInt32(&p.capacity)) 104 | } 105 | 106 | // Release Closed this pool 107 | func (p *PoolWithFunc) Release() error { 108 | p.once.Do(func() { 109 | p.release <- sig{} 110 | running := p.Running() 111 | for i := 0; i < running; i++ { 112 | p.getWorker().stop() 113 | } 114 | for i := range p.workers{ 115 | p.workers[i] = nil 116 | } 117 | }) 118 | return nil 119 | } 120 | 121 | // ReSize change the capacity of this pool 122 | func (p *PoolWithFunc) ReSize(size int) { 123 | if size < p.Cap() { 124 | diff := p.Cap() - size 125 | for i := 0; i < diff; i++ { 126 | p.getWorker().stop() 127 | } 128 | } else if size == p.Cap() { 129 | return 130 | } 131 | atomic.StoreInt32(&p.capacity, int32(size)) 132 | } 133 | 134 | //------------------------------------------------------------------------- 135 | 136 | // getWorker returns a available worker to run the tasks. 137 | func (p *PoolWithFunc) getWorker() *WorkerWithFunc { 138 | var w *WorkerWithFunc 139 | waiting := false 140 | 141 | p.lock.Lock() 142 | workers := p.workers 143 | n := len(workers) - 1 144 | if n < 0 { 145 | if p.running >= p.capacity { 146 | waiting = true 147 | } else { 148 | p.running++ 149 | } 150 | } else { 151 | <-p.freeSignal 152 | w = workers[n] 153 | workers[n] = nil 154 | p.workers = workers[:n] 155 | } 156 | p.lock.Unlock() 157 | 158 | if waiting { 159 | <-p.freeSignal 160 | p.lock.Lock() 161 | workers = p.workers 162 | l := len(workers) - 1 163 | w = workers[l] 164 | workers[l] = nil 165 | p.workers = workers[:l] 166 | p.lock.Unlock() 167 | } else if w == nil { 168 | w = &WorkerWithFunc{ 169 | pool: p, 170 | args: make(chan interface{}), 171 | } 172 | w.run() 173 | } 174 | return w 175 | } 176 | 177 | // putWorker puts a worker back into free pool, recycling the goroutines. 178 | func (p *PoolWithFunc) putWorker(worker *WorkerWithFunc) { 179 | p.lock.Lock() 180 | p.workers = append(p.workers, worker) 181 | p.lock.Unlock() 182 | p.freeSignal <- sig{} 183 | } 184 | -------------------------------------------------------------------------------- /worker.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants 24 | 25 | import ( 26 | "sync/atomic" 27 | ) 28 | 29 | // Worker is the actual executor who runs the tasks, 30 | // it starts a goroutine that accepts tasks and 31 | // performs function calls. 32 | type Worker struct { 33 | // pool who owns this worker. 34 | pool *Pool 35 | 36 | // task is a job should be done. 37 | task chan f 38 | } 39 | 40 | // run starts a goroutine to repeat the process 41 | // that performs the function calls. 42 | func (w *Worker) run() { 43 | //atomic.AddInt32(&w.pool.running, 1) 44 | go func() { 45 | for f := range w.task { 46 | if f == nil { 47 | atomic.AddInt32(&w.pool.running, -1) 48 | return 49 | } 50 | f() 51 | w.pool.putWorker(w) 52 | } 53 | }() 54 | } 55 | 56 | // stop this worker. 57 | func (w *Worker) stop() { 58 | w.sendTask(nil) 59 | } 60 | 61 | // sendTask sends a task to this worker. 62 | func (w *Worker) sendTask(task f) { 63 | w.task <- task 64 | } 65 | -------------------------------------------------------------------------------- /worker_func.go: -------------------------------------------------------------------------------- 1 | // MIT License 2 | 3 | // Copyright (c) 2018 Andy Pan 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 | 23 | package ants 24 | 25 | import ( 26 | "sync/atomic" 27 | ) 28 | 29 | // WorkerWithFunc is the actual executor who runs the tasks, 30 | // it starts a goroutine that accepts tasks and 31 | // performs function calls. 32 | type WorkerWithFunc struct { 33 | // pool who owns this worker. 34 | pool *PoolWithFunc 35 | 36 | // args is a job should be done. 37 | args chan interface{} 38 | } 39 | 40 | // run starts a goroutine to repeat the process 41 | // that performs the function calls. 42 | func (w *WorkerWithFunc) run() { 43 | //atomic.AddInt32(&w.pool.running, 1) 44 | go func() { 45 | for args := range w.args { 46 | if args == nil { 47 | atomic.AddInt32(&w.pool.running, -1) 48 | return 49 | } 50 | w.pool.poolFunc(args) 51 | w.pool.putWorker(w) 52 | } 53 | }() 54 | } 55 | 56 | // stop this worker. 57 | func (w *WorkerWithFunc) stop() { 58 | w.sendTask(nil) 59 | } 60 | 61 | // sendTask sends a task to this worker. 62 | func (w *WorkerWithFunc) sendTask(args interface{}) { 63 | w.args <- args 64 | } 65 | --------------------------------------------------------------------------------