├── 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 | [](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 | 
145 |
146 |
147 |
148 | ### Benchmarks with PoolWithFunc
149 |
150 | 
151 |
152 | ### Throughput
153 |
154 | #### 10w tasks
155 |
156 | 
157 |
158 | #### 100w tasks
159 |
160 | 
161 |
162 | #### 1000W tasks
163 |
164 | 
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] [](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 | 
149 |
150 |
151 |
152 | ### Benchmarks with PoolWithFunc
153 |
154 | 
155 |
156 | ### 吞吐量测试
157 |
158 | #### 10w 任务量
159 |
160 | 
161 |
162 | #### 100w 任务量
163 |
164 | 
165 |
166 | #### 1000w 任务量
167 |
168 | 
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 |
--------------------------------------------------------------------------------