Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion FUTURE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
## ✒ 未来版本的新特性 (Features in future versions)

### v0.2.x

* [x] 增加异步任务执行器
* [x] 支持轮询调度策略
* [ ] 支持随机调度策略
* [ ] 支持任务数量优先调度策略
* [ ] 支持任务时间优先调度策略
* [x] 支持设置 worker 任务队列大小
* [x] 支持设置 panic 处理函数
* [x] 支持自定义 sync.Locker 实现
* [ ] 完善单元测试,将覆盖率提高到 95% 以上

### v0.1.0

* [x] 增加支持退避策略的自旋锁
* [ ] 增加轻量协程库

### v0.0.1

Expand Down
11 changes: 11 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
## ✒ 历史版本的特性介绍 (Features in old versions)

### v0.2.0-alpha

> 此版本发布于 2025-06-25

* 增加异步任务执行器
* 支持轮询调度策略
* 支持设置 worker 任务队列大小
* 支持设置 panic 处理函数
* 支持自定义 sync.Locker 实现
* 单元测试覆盖率提高到 94%

### v0.1.0

> 此版本发布于 2025-06-21
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
all: test

test:
go test -cover ./...
go test -v -cover ./...

bench:
go test -v ./_examples/performance_test.go -run=none -bench=. -benchmem -benchtime=1s

fmt:
go fmt ./...
51 changes: 45 additions & 6 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@
[![Coverage](_icons/coverage.svg)](./_icons/coverage.svg)
![Test](https://github.com/FishGoddess/goes/actions/workflows/test.yml/badge.svg)

**Goes** is a easy-to-use and lightweight lib for limiting goroutines.
**Goes** is a easy-to-use and lightweight lib for executing async tasks.

[阅读中文版的文档](./README.md)

### 🥇 Features

* Spin lock with backoff strategy.
* Limiter only limits the number of simultaneous goroutines, not reuses goroutines.
* Supports spin lock with backoff strategy.
* Limits the number of simultaneous goroutines and not reuses them by Limiter.
* Limits the number of simultaneous goroutines and reuses them by Executor.

_Check [HISTORY.md](./HISTORY.md) and [FUTURE.md](./FUTURE.md) to know about more information._

### 🚀 How to use

```bash
$ go get -u github.com/FishGoddess/goes
```

```go
package main
Expand All @@ -31,21 +34,57 @@ import (
)

func main() {
// Limits the number of simultaneous goroutines and not reuses them.
limiter := goes.NewLimiter(4)

for i := 0; i < 100; i++ {
for i := 0; i < 20; i++ {
limiter.Go(func() {
fmt.Println(time.Now())
time.Sleep(100 * time.Millisecond)
fmt.Println("limiter --> ", time.Now())
time.Sleep(time.Second)
})
}

limiter.Wait()

// Limits the number of simultaneous goroutines and reuses them.
executor := goes.NewExecutor(4)
defer executor.Close()

for i := 0; i < 20; i++ {
executor.Submit(func() {
fmt.Println("executor --> ", time.Now())
time.Sleep(time.Second)
})
}
}
```

_Check more examples in [_examples](./_examples)._

### 🔨 Benchmarks

```bash
$ make bench
```

```bash
goos: linux
goarch: amd64
cpu: AMD EPYC 7K62 48-Core Processor

BenchmarkLimiter-2 2417040 498.5 ns/op 24 B/op 1 allocs/op
BenchmarkExecutor-2 23793781 49.9 ns/op 0 B/op 0 allocs/op
BenchmarkAntsPool-2 4295964 271.7 ns/op 0 B/op 0 allocs/op

BenchmarkLimiterTime-2: num is 1000000, cost is 300.936441ms
BenchmarkExecutorTime-2: num is 1000000, cost is 51.350509ms
BenchmarkAntsPoolTime-2: num is 999744, cost is 346.972287ms
```

> Obviously, goes.Executor is 5x faster than ants.Pool which has more features, so try goes if you prefer a lightweight and faster executor.

> Benchmarks: [_examples/performance_test.go](./_examples/performance_test.go).

### 👥 Contributing

If you find that something is not working as expected please open an _**issue**_.
51 changes: 45 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,23 @@
[![Coverage](_icons/coverage.svg)](./_icons/coverage.svg)
![Test](https://github.com/FishGoddess/goes/actions/workflows/test.yml/badge.svg)

**Goes** 是一个简单易用且轻量的协程数限制库
**Goes** 是一个简单易用且轻量的异步任务执行库

[Read me in English](./README.en.md)

### 🥇 功能特性

* 支持退避策略的自旋锁。
* Limiter 只限制同时执行的协程数,不复用协程。
* 内置退避策略的自旋锁。
* 支持只限制同时执行的协程数,但不复用协程,使用 Limiter。
* 支持限制同时执行的协程数,且复用协程,使用 Executor。

_历史版本的特性请查看 [HISTORY.md](./HISTORY.md)。未来版本的新特性和计划请查看 [FUTURE.md](./FUTURE.md)。_

### 🚀 使用方式

```bash
$ go get -u github.com/FishGoddess/goes
```

```go
package main
Expand All @@ -31,21 +34,57 @@ import (
)

func main() {
// Limits the number of simultaneous goroutines and not reuses them.
limiter := goes.NewLimiter(4)

for i := 0; i < 100; i++ {
for i := 0; i < 20; i++ {
limiter.Go(func() {
fmt.Println(time.Now())
time.Sleep(100 * time.Millisecond)
fmt.Println("limiter --> ", time.Now())
time.Sleep(time.Second)
})
}

limiter.Wait()

// Limits the number of simultaneous goroutines and reuses them.
executor := goes.NewExecutor(4)
defer executor.Close()

for i := 0; i < 20; i++ {
executor.Submit(func() {
fmt.Println("executor --> ", time.Now())
time.Sleep(time.Second)
})
}
}
```

_更多使用案例请查看 [_examples](./_examples) 目录。_

### 🔨 性能测试

```bash
$ make bench
```

```bash
goos: linux
goarch: amd64
cpu: AMD EPYC 7K62 48-Core Processor

BenchmarkLimiter-2 2417040 498.5 ns/op 24 B/op 1 allocs/op
BenchmarkExecutor-2 23793781 49.9 ns/op 0 B/op 0 allocs/op
BenchmarkAntsPool-2 4295964 271.7 ns/op 0 B/op 0 allocs/op

BenchmarkLimiterTime-2: num is 1000000, cost is 300.936441ms
BenchmarkExecutorTime-2: num is 1000000, cost is 51.350509ms
BenchmarkAntsPoolTime-2: num is 999744, cost is 346.972287ms
```

> 很明显,goes.Executor 的性能比功能更丰富的 ants.Pool 要高出 5 倍左右,所以当你需要一个很轻量且高性能的异步任务执行器时,可以尝试下 goes。

> 测试文件:[_examples/performance_test.go](./_examples/performance_test.go)。

### 👥 贡献者

如果您觉得 goes 缺少您需要的功能,请不要犹豫,马上参与进来,发起一个 _**issue**_。
18 changes: 15 additions & 3 deletions _examples/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,26 @@ import (
)

func main() {
// Limits the number of simultaneous goroutines and not reuses them.
limiter := goes.NewLimiter(4)

for i := 0; i < 100; i++ {
for i := 0; i < 20; i++ {
limiter.Go(func() {
fmt.Println(time.Now())
time.Sleep(100 * time.Millisecond)
fmt.Println("limiter --> ", time.Now())
time.Sleep(time.Second)
})
}

limiter.Wait()

// Limits the number of simultaneous goroutines and reuses them.
executor := goes.NewExecutor(4)
defer executor.Close()

for i := 0; i < 20; i++ {
executor.Submit(func() {
fmt.Println("executor --> ", time.Now())
time.Sleep(time.Second)
})
}
}
142 changes: 142 additions & 0 deletions _examples/performance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2025 FishGoddess. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package main

import (
"sync/atomic"
"testing"
"time"

"github.com/FishGoddess/goes"
//"github.com/panjf2000/ants/v2"
)

const (
limit = 256
workerNum = limit
size = limit
timeLoop = 100_0000
)

func bench(num *uint32) {
atomic.AddUint32(num, 1)
}

// go test -v -run=none -bench=^BenchmarkLimiter$ -benchmem -benchtime=1s
func BenchmarkLimiter(b *testing.B) {
limiter := goes.NewLimiter(limit)

num := uint32(0)
f := func() {
bench(&num)
}

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
limiter.Go(f)
}
})

limiter.Wait()
b.Logf("num is %d", num)
}

// go test -v -run=none -bench=^BenchmarkLimiterTime$ -benchmem -benchtime=1s
func BenchmarkLimiterTime(b *testing.B) {
limiter := goes.NewLimiter(limit)

num := uint32(0)
f := func() {
bench(&num)
}

beginTime := time.Now()
for range timeLoop {
limiter.Go(f)
}

limiter.Wait()

cost := time.Since(beginTime)
b.Logf("num is %d, cost is %s", num, cost)
}

// go test -v -run=none -bench=^BenchmarkExecutor$ -benchmem -benchtime=1s
func BenchmarkExecutor(b *testing.B) {
executor := goes.NewExecutor(workerNum)

num := uint32(0)
task := func() {
bench(&num)
}

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
executor.Submit(task)
}
})

executor.Close()
b.Logf("num is %d", num)
}

// go test -v -run=none -bench=^BenchmarkExecutorTime$ -benchmem -benchtime=1s
func BenchmarkExecutorTime(b *testing.B) {
executor := goes.NewExecutor(size)

num := uint32(0)
task := func() {
bench(&num)
}

beginTime := time.Now()
for range timeLoop {
executor.Submit(task)
}

executor.Close()

cost := time.Since(beginTime)
b.Logf("num is %d, cost is %s", num, cost)
}

// // go test -v -run=none -bench=^BenchmarkAntsPool$ -benchmem -benchtime=1s
// func BenchmarkAntsPool(b *testing.B) {
// pool, _ := ants.NewPool(size)
//
// num := uint32(0)
// task := func() {
// bench(&num)
// }
//
// b.RunParallel(func(pb *testing.PB) {
// for pb.Next() {
// pool.Submit(task)
// }
// })
//
// pool.Release()
// b.Logf("num is %d", num)
// }
//
// // go test -v -run=none -bench=^BenchmarkAntsPoolTime$ -benchmem -benchtime=1s
// func BenchmarkAntsPoolTime(b *testing.B) {
// pool, _ := ants.NewPool(size)
//
// num := uint32(0)
// task := func() {
// bench(&num)
// }
//
// beginTime := time.Now()
// for range timeLoop {
// pool.Submit(task)
// }
//
// pool.Release()
//
// cost := time.Since(beginTime)
// b.Logf("num is %d, cost is %s", num, cost)
// }
Loading
Loading