├── .vscode └── database.json ├── .gitignore ├── sample └── grpool.go ├── gorpoor ├── main.go ├── v2 ├── gorpoor_test.go └── gorpoor.go ├── v1 ├── gorpoor_test.go └── gorpoor.go ├── v3 ├── gorpoor_test.go └── gorpoor.go ├── go.json └── README.md /.vscode/database.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | gorpoor 3 | -------------------------------------------------------------------------------- /sample/grpool.go: -------------------------------------------------------------------------------- 1 | package sample 2 | -------------------------------------------------------------------------------- /gorpoor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldtree/gorpoor/HEAD/gorpoor -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | func main() { 10 | sc := make(chan os.Signal, 1) 11 | signal.Notify(sc, 12 | syscall.SIGINT, 13 | syscall.SIGTERM, 14 | syscall.SIGQUIT, 15 | ) 16 | <-sc 17 | } 18 | -------------------------------------------------------------------------------- /v2/gorpoor_test.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | //"log" 5 | "sync" 6 | "sync/atomic" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var result int64 12 | var wg sync.WaitGroup 13 | 14 | type MyTask struct { 15 | Id int64 16 | } 17 | 18 | func (m *MyTask) Exec() error { 19 | atomic.AddInt64(&result, m.Id) 20 | wg.Done() 21 | return nil 22 | } 23 | 24 | func Test_process(t *testing.T) { 25 | w := new(WorkerPool) 26 | w.Init(10, 10) 27 | go w.Start() 28 | time.Sleep(time.Second * 2) 29 | w.Stop() 30 | } 31 | 32 | func Test_addtask(t *testing.T) { 33 | w := new(WorkerPool) 34 | w.Init(10, 10) 35 | go w.Start() 36 | for index := 0; index < 1000; index++ { 37 | wg.Add(1) 38 | w.AddTask(&MyTask{Id: int64(index)}) 39 | } 40 | wg.Wait() 41 | //log.Println("result : ", result) 42 | w.Stop() 43 | } 44 | 45 | func Benchmark_work_10_10(b *testing.B) { 46 | w := new(WorkerPool) 47 | w.Init(10, 10) 48 | go w.Start() 49 | 50 | for index := 0; index < b.N; index++ { 51 | wg.Add(1) 52 | w.AddTask(&MyTask{Id: int64(index)}) 53 | } 54 | wg.Wait() 55 | //log.Println("result : ", result) 56 | w.Stop() 57 | } 58 | 59 | func Benchmark_work_10_100(b *testing.B) { 60 | w := new(WorkerPool) 61 | w.Init(10, 100) 62 | go w.Start() 63 | for index := 0; index < b.N; index++ { 64 | wg.Add(1) 65 | w.AddTask(&MyTask{Id: int64(index)}) 66 | } 67 | wg.Wait() 68 | //log.Println("result : ", result) 69 | w.Stop() 70 | } 71 | 72 | func Benchmark_work_100_100(b *testing.B) { 73 | w := new(WorkerPool) 74 | w.Init(100, 100) 75 | go w.Start() 76 | for index := 0; index < b.N; index++ { 77 | wg.Add(1) 78 | w.AddTask(&MyTask{Id: int64(index)}) 79 | } 80 | wg.Wait() 81 | //log.Println("result : ", result) 82 | w.Stop() 83 | } 84 | 85 | func Benchmark_work_100_1000(b *testing.B) { 86 | w := new(WorkerPool) 87 | w.Init(10, 1000) 88 | go w.Start() 89 | 90 | for index := 0; index < b.N; index++ { 91 | wg.Add(1) 92 | w.AddTask(&MyTask{Id: int64(index)}) 93 | } 94 | wg.Wait() 95 | //log.Println("result : ", result) 96 | w.Stop() 97 | } 98 | 99 | func Benchmark_work_100_10000(b *testing.B) { 100 | w := new(WorkerPool) 101 | w.Init(10, 10000) 102 | go w.Start() 103 | for index := 0; index < b.N; index++ { 104 | wg.Add(1) 105 | w.AddTask(&MyTask{Id: int64(index)}) 106 | } 107 | wg.Wait() 108 | //log.Println("result : ", result) 109 | w.Stop() 110 | } 111 | 112 | func Benchmark_work_1000_10000(b *testing.B) { 113 | w := new(WorkerPool) 114 | w.Init(100, 10000) 115 | go w.Start() 116 | 117 | for index := 0; index < b.N; index++ { 118 | wg.Add(1) 119 | w.AddTask(&MyTask{Id: int64(index)}) 120 | } 121 | wg.Wait() 122 | //log.Println("result : ", result) 123 | w.Stop() 124 | } 125 | -------------------------------------------------------------------------------- /v1/gorpoor_test.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | //"log" 5 | "sync" 6 | "sync/atomic" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var result int64 12 | var wg sync.WaitGroup 13 | 14 | type MyTask struct { 15 | Id int64 16 | } 17 | 18 | func (m *MyTask) Exec() error { 19 | atomic.AddInt64(&result, m.Id) 20 | wg.Done() 21 | return nil 22 | } 23 | 24 | func Test_process(t *testing.T) { 25 | w := new(WorkerPool) 26 | w.Init(10, 10) 27 | go w.Start() 28 | time.Sleep(time.Second * 2) 29 | w.Stop() 30 | } 31 | 32 | func Test_addtask(t *testing.T) { 33 | w := new(WorkerPool) 34 | w.Init(10, 10) 35 | go w.Start() 36 | 37 | for index := 0; index < 1000; index++ { 38 | wg.Add(1) 39 | w.AddTask(&MyTask{Id: int64(index)}) 40 | } 41 | wg.Wait() 42 | //log.Println("result : ", result) 43 | w.Stop() 44 | } 45 | 46 | func Benchmark_work_10_10(b *testing.B) { 47 | w := new(WorkerPool) 48 | w.Init(10, 10) 49 | go w.Start() 50 | 51 | for index := 0; index < b.N; index++ { 52 | wg.Add(1) 53 | w.AddTask(&MyTask{Id: int64(index)}) 54 | } 55 | wg.Wait() 56 | //log.Println("result : ", result) 57 | w.Stop() 58 | } 59 | 60 | func Benchmark_work_10_100(b *testing.B) { 61 | w := new(WorkerPool) 62 | w.Init(10, 100) 63 | go w.Start() 64 | for index := 0; index < b.N; index++ { 65 | wg.Add(1) 66 | w.AddTask(&MyTask{Id: int64(index)}) 67 | } 68 | wg.Wait() 69 | //log.Println("result : ", result) 70 | w.Stop() 71 | } 72 | 73 | func Benchmark_work_100_100(b *testing.B) { 74 | w := new(WorkerPool) 75 | w.Init(100, 100) 76 | go w.Start() 77 | for index := 0; index < b.N; index++ { 78 | wg.Add(1) 79 | w.AddTask(&MyTask{Id: int64(index)}) 80 | } 81 | wg.Wait() 82 | //log.Println("result : ", result) 83 | w.Stop() 84 | } 85 | 86 | func Benchmark_work_100_1000(b *testing.B) { 87 | w := new(WorkerPool) 88 | w.Init(10, 1000) 89 | go w.Start() 90 | 91 | for index := 0; index < b.N; index++ { 92 | wg.Add(1) 93 | w.AddTask(&MyTask{Id: int64(index)}) 94 | } 95 | wg.Wait() 96 | //log.Println("result : ", result) 97 | w.Stop() 98 | } 99 | 100 | func Benchmark_work_100_10000(b *testing.B) { 101 | w := new(WorkerPool) 102 | w.Init(10, 10000) 103 | go w.Start() 104 | for index := 0; index < b.N; index++ { 105 | wg.Add(1) 106 | w.AddTask(&MyTask{Id: int64(index)}) 107 | } 108 | wg.Wait() 109 | //log.Println("result : ", result) 110 | w.Stop() 111 | } 112 | 113 | func Benchmark_work_1000_10000(b *testing.B) { 114 | w := new(WorkerPool) 115 | w.Init(100, 10000) 116 | go w.Start() 117 | 118 | for index := 0; index < b.N; index++ { 119 | wg.Add(1) 120 | w.AddTask(&MyTask{Id: int64(index)}) 121 | } 122 | wg.Wait() 123 | //log.Println("result : ", result) 124 | w.Stop() 125 | } 126 | -------------------------------------------------------------------------------- /v3/gorpoor_test.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | import ( 4 | //"log" 5 | "sync" 6 | "sync/atomic" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var result int64 12 | var wg sync.WaitGroup 13 | 14 | type MyTask struct { 15 | Id int64 16 | } 17 | 18 | func (m *MyTask) Exec() error { 19 | atomic.AddInt64(&result, m.Id) 20 | wg.Done() 21 | return nil 22 | } 23 | 24 | func Test_process(t *testing.T) { 25 | w := new(WorkerPool) 26 | w.Init(10, 10) 27 | go w.Start() 28 | time.Sleep(time.Second * 2) 29 | w.Stop() 30 | } 31 | 32 | func Test_addtask(t *testing.T) { 33 | w := new(WorkerPool) 34 | w.Init(10, 10) 35 | go w.Start() 36 | 37 | for index := 0; index < 1000; index++ { 38 | wg.Add(1) 39 | w.AddTask(&MyTask{Id: int64(index)}) 40 | } 41 | wg.Wait() 42 | //log.Println("result : ", result) 43 | w.Stop() 44 | } 45 | 46 | func Benchmark_work_10_10(b *testing.B) { 47 | w := new(WorkerPool) 48 | w.Init(10, 10) 49 | go w.Start() 50 | 51 | for index := 0; index < b.N; index++ { 52 | wg.Add(1) 53 | w.AddTask(&MyTask{Id: int64(index)}) 54 | } 55 | wg.Wait() 56 | //log.Println("result : ", result) 57 | w.Stop() 58 | } 59 | 60 | func Benchmark_work_10_100(b *testing.B) { 61 | w := new(WorkerPool) 62 | w.Init(10, 100) 63 | go w.Start() 64 | for index := 0; index < b.N; index++ { 65 | wg.Add(1) 66 | w.AddTask(&MyTask{Id: int64(index)}) 67 | } 68 | wg.Wait() 69 | //log.Println("result : ", result) 70 | w.Stop() 71 | } 72 | 73 | func Benchmark_work_100_100(b *testing.B) { 74 | w := new(WorkerPool) 75 | w.Init(100, 100) 76 | go w.Start() 77 | for index := 0; index < b.N; index++ { 78 | wg.Add(1) 79 | w.AddTask(&MyTask{Id: int64(index)}) 80 | } 81 | wg.Wait() 82 | //log.Println("result : ", result) 83 | w.Stop() 84 | } 85 | 86 | func Benchmark_work_100_1000(b *testing.B) { 87 | w := new(WorkerPool) 88 | w.Init(10, 1000) 89 | go w.Start() 90 | 91 | for index := 0; index < b.N; index++ { 92 | wg.Add(1) 93 | w.AddTask(&MyTask{Id: int64(index)}) 94 | } 95 | wg.Wait() 96 | //log.Println("result : ", result) 97 | w.Stop() 98 | } 99 | 100 | func Benchmark_work_100_10000(b *testing.B) { 101 | w := new(WorkerPool) 102 | w.Init(10, 10000) 103 | go w.Start() 104 | for index := 0; index < b.N; index++ { 105 | wg.Add(1) 106 | w.AddTask(&MyTask{Id: int64(index)}) 107 | } 108 | wg.Wait() 109 | //log.Println("result : ", result) 110 | w.Stop() 111 | } 112 | 113 | func Benchmark_work_1000_10000(b *testing.B) { 114 | w := new(WorkerPool) 115 | w.Init(100, 10000) 116 | go w.Start() 117 | 118 | for index := 0; index < b.N; index++ { 119 | wg.Add(1) 120 | w.AddTask(&MyTask{Id: int64(index)}) 121 | } 122 | wg.Wait() 123 | //log.Println("result : ", result) 124 | w.Stop() 125 | } 126 | -------------------------------------------------------------------------------- /go.json: -------------------------------------------------------------------------------- 1 | { 2 | /* 3 | // Place your snippets for Go here. Each snippet is defined under a snippet name and has a prefix, body and 4 | // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 5 | // $1, $2 for tab stops, ${id} and ${id:label} and ${1:label} for variables. Variables with the same id are connected. 6 | // Example: 7 | "Print to console": { 8 | "prefix": "log", 9 | "body": [ 10 | "console.log('$1');", 11 | "$2" 12 | ], 13 | "description": "Log output to console" 14 | } 15 | */ 16 | "return":{ 17 | "prefix": "re", 18 | "body": "return", 19 | "description": "return content" 20 | }, 21 | "for key ,value:":{ 22 | "prefix":"for k", 23 | "body": "for key,value:=range {\n\n}", 24 | "description": "for range key,value" 25 | }, 26 | "for select range":{ 27 | "prefix": "for s", 28 | "body": "for{\n select {\n\n} \n}", 29 | "description": "for select statement" 30 | }, 31 | "defer recover":{ 32 | "prefix": "defer r", 33 | "body": "defer func() { \nif re := recover(); re != nil {\nlog.Error(\"recover panic : \", re)\n}\n}()", 34 | "description": "defer recover function" 35 | }, 36 | "make map":{ 37 | "prefix": "make m", 38 | "body": "make(map[interface{}]interface{})", 39 | "description": "make map" 40 | }, 41 | "comment":{ 42 | "prefix": "comment", 43 | "body": "/*******************\n\n\n\n*********************/", 44 | "description": "user comment" 45 | }, 46 | "copyright":{ 47 | "prefix": "copyright", 48 | "body": "// Copyright 2016 The Grapes. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.", 49 | "description": "Copyright declare" 50 | }, 51 | "if m":{ 52 | "prefix": "if m", 53 | "body": "if value,ok:= map[interface{}]interface{};ok{ \r\n }", 54 | "description": "判断map并生成if语句" 55 | }, 56 | "if hachha":{ 57 | "prefix": "if chan", 58 | "body": "if value ,ok :=range <-chan ;ok{ \r\n }", 59 | "description": "" 60 | }, 61 | 62 | "signal":{ 63 | "prefix": "signal", 64 | "body": [ 65 | "sc := make(chan os.Signal, 1)", 66 | "signal.Notify(sc,", 67 | "syscall.SIGINT,", 68 | "syscall.SIGTERM,", 69 | "syscall.SIGQUIT,", 70 | ")", 71 | "<-sc" 72 | ] 73 | }, 74 | 75 | "for chan":{ 76 | "prefix": "for chan", 77 | "body": [ 78 | "for ok,value:=range <- value chan;ok{", 79 | //here to do 80 | "}" 81 | ], 82 | "description": "for chan loop" 83 | }, 84 | 85 | "switch":{ 86 | "prefix": "switch e", 87 | "body": "switch e:=e.(type){\n default:\n // error is nil \n case error:\n \\error os non-nil \n return e \n }", 88 | "description": "根据error.(type)来判定错误的处理方式" 89 | }, 90 | 91 | "package mo":{ 92 | "prefix": "package mo", 93 | "body": "", 94 | "description": "" 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gorpoor goroutine pool 2 | 3 | > 一个 goroutine 协程池的实现 ,所有的操作都是预先分配好可以执行的goroutine,避免在连续的实际任务执行中启动新的协程所带来的损耗 4 | 5 | > a goroutine pool implements 6 | 7 | > 使用三种方式实现一个可用的goroutine pool: 8 | > * 一个全局的task队列,然后每一个worker使用注册的回调函数来从全局获取任务并阻塞执行,任务的分发依靠go runtime的调度机制,每一个worker不用维持自己的 9 | > task队列这样从 主任务 到 worker任务就只有两层,比较简单,worker是不可选的,worker可以执行新的任务时,就必须执行,没有可以做屏蔽的东西(不支持将一个 10 | > 不想去执行的task重新塞入主任务的task队列中);这样只要投送任务,直至任务队列被填满,但是worker却是有限的 11 | 12 | go test -bench . 13 | goos: darwin 14 | goarch: amd64 15 | pkg: github.com/FlyCynomys/tools/gorpoor/v1 16 | Benchmark_work_10_10-4 3000000 635 ns/op 17 | Benchmark_work_10_100-4 3000000 445 ns/op 18 | Benchmark_work_100_100-4 3000000 430 ns/op 19 | Benchmark_work_100_1000-4 3000000 404 ns/op 20 | Benchmark_work_100_10000-4 3000000 468 ns/op 21 | Benchmark_work_1000_10000-4 3000000 419 ns/op 22 | PASS 23 | ok github.com/FlyCynomys/tools/gorpoor/v1 14.539s 24 | 25 | 26 | 27 | > * 一个全局的task队列,然后每一个worker在task来到时,被主任务取出并分配任务去执行,执行完结束后使用注册的回调函数来将自己重新入队列,这样worker不断地取出 28 | > 执行的循环,这样只在worker侧保存一个正在执行的task,调度将是齿轮式的,投送任务通过 主任务 来进行,也只是分两层的 主任务 和 worker 29 | 30 | go test -bench . 31 | goos: darwin 32 | goarch: amd64 33 | pkg: github.com/FlyCynomys/tools/gorpoor/v2 34 | Benchmark_work_10_10-4 500000 2138 ns/op 35 | Benchmark_work_10_100-4 1000000 1412 ns/op 36 | Benchmark_work_100_100-4 1000000 1349 ns/op 37 | Benchmark_work_100_1000-4 500000 2851 ns/op 38 | Benchmark_work_100_10000-4 1000000 2102 ns/op 39 | Benchmark_work_1000_10000-4 1000000 2176 ns/op 40 | PASS 41 | ok github.com/FlyCynomys/tools/gorpoor/v2 12.613s 42 | 43 | > * 一个全局的task队列,来接受任务,每一个worker也有自己本地的任务队列,每一个worker维持一定数量的goroutine,从worker本地的队列取出任务并执行,并实时更新 44 | > worker本身的状态,在主任务中可以通过数组来保存worker队列,也可以使用一个buffer channel来保存,通过不断地更新数组状态或者队列的取出放入操作选择合适的 45 | > worker来派送task去worker本地的队列,当有新任务进来worker的本地队列,或者worker执行完一个task都更新自己的状态,这样就有三层了 46 | > main-task---->worker-task--->real-goroutine 47 | 48 | go test -bench . 49 | goos: darwin 50 | goarch: amd64 51 | pkg: github.com/FlyCynomys/tools/gorpoor/v3 52 | Benchmark_work_10_10-4 1000000 1484 ns/op 53 | Benchmark_work_10_100-4 2000000 827 ns/op 54 | Benchmark_work_100_100-4 1000000 1155 ns/op 55 | Benchmark_work_100_1000-4 2000000 776 ns/op 56 | Benchmark_work_100_10000-4 2000000 721 ns/op 57 | Benchmark_work_1000_10000-4 2000000 798 ns/op 58 | PASS 59 | ok github.com/FlyCynomys/tools/gorpoor/v3 13.930s 60 | -------------------------------------------------------------------------------- /v1/gorpoor.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | "sync" 7 | "sync/atomic" 8 | ) 9 | 10 | const ( 11 | STATUS_INIT = iota 12 | STATUS_START 13 | STATUS_RUNNING 14 | STATUS_STOP 15 | ) 16 | 17 | var ( 18 | ErrWorkPoolIsRuning = errors.New("work pool is running") 19 | ) 20 | 21 | func Exec() error { 22 | log.Println("music") 23 | return nil 24 | } 25 | 26 | type Execer func() error 27 | 28 | func (exec Execer) Exec() error { 29 | return exec() 30 | } 31 | 32 | type Tasker interface { 33 | Exec() error 34 | } 35 | 36 | type Task struct { 37 | Params interface{} 38 | Result interface{} 39 | } 40 | 41 | func (t *Task) Exec() error { 42 | log.Println("task exec result ok") 43 | return nil 44 | } 45 | 46 | type Worker struct { 47 | WorkerId int64 48 | StopChan chan struct{} 49 | TaskList chan Tasker 50 | 51 | Status int64 52 | Wg *sync.WaitGroup 53 | } 54 | 55 | func (w *Worker) Init(index int64) { 56 | atomic.StoreInt64(&w.Status, STATUS_INIT) 57 | w.WorkerId = index 58 | w.Wg.Add(1) 59 | atomic.StoreInt64(&w.Status, STATUS_START) 60 | } 61 | 62 | func (w *Worker) Start() { 63 | defer func() { 64 | w.Stop() 65 | //log.Printf("worker [%d] is end statu [%d]\n", w.WorkerId, w.Status) 66 | }() 67 | if atomic.LoadInt64(&w.Status) == STATUS_START { 68 | atomic.StoreInt64(&w.Status, STATUS_RUNNING) 69 | } 70 | var err error 71 | for { 72 | select { 73 | case <-w.StopChan: 74 | goto END 75 | case t, ok := <-w.TaskList: 76 | 77 | if ok && t != nil { 78 | err = t.Exec() 79 | if err != nil { 80 | log.Println("exec task error", err.Error()) 81 | } 82 | } else { 83 | goto END 84 | } 85 | } 86 | } 87 | END: 88 | return 89 | } 90 | 91 | func (w *Worker) Stop() { 92 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 93 | return 94 | } 95 | atomic.StoreInt64(&w.Status, STATUS_STOP) 96 | w.Wg.Done() 97 | return 98 | } 99 | 100 | type WorkerPool struct { 101 | TaskList chan Tasker 102 | StopChan chan struct{} 103 | 104 | WorkQueue []*Worker 105 | 106 | Status int64 107 | Wg *sync.WaitGroup 108 | 109 | Protect sync.Mutex 110 | } 111 | 112 | func (w *WorkerPool) Init(number int, taskLength int) { 113 | w.TaskList = make(chan Tasker, taskLength) 114 | w.StopChan = make(chan struct{}, 1) 115 | atomic.StoreInt64(&w.Status, STATUS_INIT) 116 | w.WorkQueue = make([]*Worker, number, number) 117 | w.Wg = new(sync.WaitGroup) 118 | for index, _ := range w.WorkQueue { 119 | newWorker := new(Worker) 120 | newWorker.Wg = w.Wg 121 | newWorker.StopChan = w.StopChan 122 | newWorker.TaskList = w.TaskList 123 | newWorker.Init(int64(index)) 124 | w.WorkQueue[index] = newWorker 125 | go w.WorkQueue[index].Start() 126 | } 127 | 128 | atomic.StoreInt64(&w.Status, STATUS_INIT) 129 | return 130 | } 131 | func (w *WorkerPool) Start() { 132 | defer func() { 133 | }() 134 | for { 135 | select { 136 | case <-w.StopChan: 137 | goto END 138 | } 139 | } 140 | END: 141 | return 142 | } 143 | func (w *WorkerPool) Stop() { 144 | w.Protect.Lock() 145 | defer w.Protect.Unlock() 146 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 147 | return 148 | } 149 | w.StopChan <- struct{}{} 150 | atomic.StoreInt64(&w.Status, STATUS_STOP) 151 | close(w.StopChan) 152 | close(w.TaskList) 153 | w.Wg.Wait() 154 | return 155 | } 156 | 157 | func (w *WorkerPool) AddTask(t Tasker) { 158 | w.Protect.Lock() 159 | defer w.Protect.Unlock() 160 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 161 | return 162 | } 163 | w.TaskList <- t 164 | return 165 | } 166 | -------------------------------------------------------------------------------- /v3/gorpoor.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | import ( 4 | "log" 5 | "math/rand" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | ) 10 | 11 | const ( 12 | STATUS_INIT = iota 13 | STATUS_START 14 | STATUS_RUNNING 15 | STATUS_STOP 16 | ) 17 | 18 | type Execer func() error 19 | 20 | func (exec Execer) Exec() error { 21 | return exec() 22 | } 23 | 24 | var randomWorld = rand.New(rand.NewSource(time.Now().Unix())) 25 | 26 | type Tasker interface { 27 | Exec() error 28 | } 29 | 30 | type Task struct { 31 | Params interface{} 32 | Result interface{} 33 | } 34 | 35 | func (t *Task) Exec() error { 36 | log.Printf("start exec task : %p \n", t) 37 | return nil 38 | } 39 | 40 | type Worker struct { 41 | WorkerId int64 42 | TaskList chan Tasker 43 | Status int64 44 | StopChan chan struct{} 45 | Wg *sync.WaitGroup 46 | } 47 | 48 | func (w *Worker) Init(index int, taskLength int) { 49 | w.WorkerId = int64(index) 50 | w.TaskList = make(chan Tasker, taskLength) 51 | w.Wg.Add(1) 52 | atomic.StoreInt64(&w.Status, STATUS_INIT) 53 | } 54 | 55 | func (w *Worker) Start() { 56 | defer func() { 57 | w.Stop() 58 | }() 59 | var err error 60 | for { 61 | select { 62 | case <-w.StopChan: 63 | goto END 64 | case t, ok := <-w.TaskList: 65 | if ok { 66 | err = t.Exec() 67 | if err != nil { 68 | log.Println("exec task failed : ", err.Error()) 69 | } 70 | } else { 71 | goto END 72 | } 73 | } 74 | } 75 | END: 76 | return 77 | } 78 | 79 | func (w *Worker) Stop() { 80 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 81 | return 82 | } 83 | atomic.StoreInt64(&w.Status, STATUS_STOP) 84 | close(w.TaskList) 85 | w.Wg.Done() 86 | } 87 | 88 | type WorkerPool struct { 89 | WorkNumber int 90 | WorkSlice []*Worker 91 | TaskList chan Tasker 92 | 93 | Status int64 94 | 95 | StopChan chan struct{} 96 | Wg *sync.WaitGroup 97 | } 98 | 99 | func (w *WorkerPool) Init(number int, taskLength int) { 100 | atomic.StoreInt64(&w.Status, STATUS_STOP) 101 | w.WorkNumber = number 102 | w.WorkSlice = make([]*Worker, w.WorkNumber, w.WorkNumber) 103 | w.Wg = new(sync.WaitGroup) 104 | w.StopChan = make(chan struct{}, 1) 105 | w.TaskList = make(chan Tasker, taskLength) 106 | for index, _ := range w.WorkSlice { 107 | newWorker := new(Worker) 108 | newWorker.Wg = w.Wg 109 | newWorker.Init(index, taskLength) 110 | newWorker.StopChan = w.StopChan 111 | w.WorkSlice[index] = newWorker 112 | go newWorker.Start() 113 | } 114 | atomic.StoreInt64(&w.Status, STATUS_INIT) 115 | return 116 | } 117 | 118 | func (w *WorkerPool) AddTask(task Tasker) { 119 | if atomic.LoadInt64(&w.Status) != STATUS_STOP { 120 | w.TaskList <- task 121 | return 122 | } 123 | log.Panic("error operation on stop pool") 124 | return 125 | } 126 | 127 | func (w *WorkerPool) Start() { 128 | atomic.StoreInt64(&w.Status, STATUS_START) 129 | defer func() { 130 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 131 | return 132 | } else { 133 | w.Stop() 134 | } 135 | }() 136 | for { 137 | select { 138 | case <-w.StopChan: 139 | goto END 140 | case t, ok := <-w.TaskList: 141 | if ok && t != nil { 142 | index := randomWorld.Int31n(int32(w.WorkNumber)) 143 | w.WorkSlice[int(index)].TaskList <- t 144 | } else { 145 | goto END 146 | } 147 | } 148 | } 149 | END: 150 | return 151 | } 152 | 153 | func (w *WorkerPool) Stop() { 154 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 155 | return 156 | } 157 | atomic.StoreInt64(&w.Status, STATUS_STOP) 158 | w.StopChan <- struct{}{} 159 | close(w.StopChan) 160 | w.Wg.Wait() 161 | return 162 | } 163 | -------------------------------------------------------------------------------- /v2/gorpoor.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | "sync" 7 | "sync/atomic" 8 | ) 9 | 10 | const ( 11 | STATUS_INIT = iota 12 | STATUS_START 13 | STATUS_RUNNING 14 | STATUS_STOP 15 | ) 16 | 17 | var ( 18 | ErrorTaskExec = errors.New("task exec error") 19 | ) 20 | 21 | type Execer func() error 22 | 23 | func (exec Execer) Exec() error { 24 | return exec() 25 | } 26 | 27 | type Tasker interface { 28 | Exec() error 29 | } 30 | 31 | type Task struct { 32 | Params interface{} 33 | Result interface{} 34 | } 35 | 36 | type Worker struct { 37 | WorkerId int64 38 | StopChan chan struct{} 39 | 40 | TaskChan chan Tasker 41 | WorkerBackChan chan *Worker 42 | 43 | Wg *sync.WaitGroup 44 | Status int64 45 | } 46 | 47 | func (w *Worker) Init(index int) { 48 | w.WorkerId = int64(index) 49 | w.TaskChan = make(chan Tasker, 1) 50 | w.WorkerBackChan <- w 51 | w.Wg.Add(1) 52 | atomic.StoreInt64(&w.WorkerId, STATUS_INIT) 53 | } 54 | 55 | func (w *Worker) Start() { 56 | defer func() { 57 | w.Stop() 58 | return 59 | }() 60 | atomic.StoreInt64(&w.Status, STATUS_RUNNING) 61 | var err error 62 | for { 63 | select { 64 | case <-w.StopChan: 65 | goto END 66 | case t, ok := <-w.TaskChan: 67 | if ok && t != nil { 68 | err = t.Exec() 69 | if err != nil { 70 | log.Println("task exec error : ", err.Error()) 71 | } 72 | w.WorkerBackChan <- w 73 | } else { 74 | goto END 75 | } 76 | 77 | } 78 | } 79 | END: 80 | return 81 | } 82 | 83 | func (w *Worker) Stop() { 84 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 85 | return 86 | } 87 | close(w.TaskChan) 88 | atomic.StoreInt64(&w.Status, STATUS_STOP) 89 | w.Wg.Done() 90 | return 91 | } 92 | 93 | type WorkerPool struct { 94 | TaskList chan Tasker 95 | Status int64 96 | Worker chan *Worker 97 | WorkerQueue []*Worker 98 | Wg *sync.WaitGroup 99 | Protect sync.Mutex 100 | StopChan chan struct{} 101 | } 102 | 103 | func (w *WorkerPool) Init(number int, taskLength int) { 104 | w.Worker = make(chan *Worker, number) 105 | w.Wg = new(sync.WaitGroup) 106 | w.WorkerQueue = make([]*Worker, number) 107 | w.StopChan = make(chan struct{}, 1) 108 | w.TaskList = make(chan Tasker, taskLength) 109 | for index, _ := range w.WorkerQueue { 110 | newWorker := new(Worker) 111 | newWorker.Wg = w.Wg 112 | newWorker.WorkerBackChan = w.Worker 113 | newWorker.StopChan = w.StopChan 114 | newWorker.Init(index) 115 | w.WorkerQueue[index] = newWorker 116 | go newWorker.Start() 117 | } 118 | atomic.StoreInt64(&w.Status, STATUS_INIT) 119 | return 120 | } 121 | 122 | func (w *WorkerPool) Start() { 123 | defer func() { 124 | //log.Println("work pool is end") 125 | }() 126 | atomic.StoreInt64(&w.Status, STATUS_START) 127 | for { 128 | select { 129 | case <-w.StopChan: 130 | goto END 131 | case t := <-w.TaskList: 132 | worker, ok := <-w.Worker 133 | if ok && worker != nil { 134 | worker.TaskChan <- t 135 | } else { 136 | goto END 137 | } 138 | } 139 | } 140 | END: 141 | return 142 | } 143 | 144 | func (w *WorkerPool) Stop() { 145 | w.Protect.Lock() 146 | defer w.Protect.Unlock() 147 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 148 | return 149 | } 150 | atomic.StoreInt64(&w.Status, STATUS_STOP) 151 | close(w.StopChan) 152 | w.Wg.Wait() 153 | close(w.TaskList) 154 | close(w.Worker) 155 | return 156 | } 157 | 158 | func (w *WorkerPool) AddTask(t Tasker) { 159 | w.Protect.Lock() 160 | defer w.Protect.Unlock() 161 | if atomic.LoadInt64(&w.Status) == STATUS_STOP { 162 | return 163 | } 164 | w.TaskList <- t 165 | return 166 | } 167 | --------------------------------------------------------------------------------