feat(counter): go-hua/single 迁入并更名 counter(键值计数器)
- 原包名 single 名不达意,实为 string→int64 并发计数器;类型 Single→Counter, 方法签名不变(Init/Get/Incr/Set),附测试、例程与 README
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
# counter
|
||||||
|
|
||||||
|
线程安全的 `string → int64` 键值计数器,适合并发计数场景(按 key 累加/读取/覆盖)。
|
||||||
|
|
||||||
|
> 迁移自 go-hua/single 并更名:包 `single` → `counter`,类型 `Single` → `Counter`,
|
||||||
|
> 方法签名不变(Init/Get/Incr/Set)。
|
||||||
|
|
||||||
|
## 用法
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.zeroonesoft.cn/golib/zogo/counter"
|
||||||
|
)
|
||||||
|
|
||||||
|
var c counter.Counter
|
||||||
|
c.Init()
|
||||||
|
|
||||||
|
// 并发安全累加
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() { defer wg.Done(); c.Incr("pv", 1) }()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
c.Get("pv") // 100
|
||||||
|
|
||||||
|
// Set 覆盖并返回旧值
|
||||||
|
old := c.Set("pv", 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
完整可运行例程:[examples/counter/main.go](../examples/counter/main.go)
|
||||||
|
|
||||||
|
## 注意
|
||||||
|
|
||||||
|
- **使用前必须 Init**,未初始化直接 `Incr/Set` 会 panic(nil map 写入)。
|
||||||
|
- 单实例内部持锁;跨实例无共享状态。
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Package counter 提供线程安全的 string→int64 键值计数器,适合并发计数场景。
|
||||||
|
//
|
||||||
|
// 迁移自 go-hua/single 并更名(原类型 Single → Counter),方法签名不变。
|
||||||
|
package counter
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// Counter 线程安全的键值计数器(使用前需调用 Init)
|
||||||
|
type Counter struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
values map[string]int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init 初始化内部存储(每个 Counter 实例使用前调用一次)
|
||||||
|
func (c *Counter) Init() {
|
||||||
|
c.values = make(map[string]int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 读取指定 key 的值,key 不存在返回 0
|
||||||
|
func (c *Counter) Get(key string) int64 {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.values[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incr 按指定增量累加 key 的值,返回累加后的值
|
||||||
|
func (c *Counter) Incr(key string, value int64) int64 {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.values[key] += value
|
||||||
|
return c.values[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set 覆盖指定 key 的值,返回旧值
|
||||||
|
func (c *Counter) Set(key string, val int64) int64 {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
old := c.values[key]
|
||||||
|
c.values[key] = val
|
||||||
|
return old
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package counter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIncrConcurrent(t *testing.T) {
|
||||||
|
var c Counter
|
||||||
|
c.Init()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
c.Incr("pv", 1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if got := c.Get("pv"); got != 100 {
|
||||||
|
t.Errorf("并发 100 次 Incr 后 = %d, want 100", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet(t *testing.T) {
|
||||||
|
var c Counter
|
||||||
|
c.Init()
|
||||||
|
|
||||||
|
c.Set("k", 10)
|
||||||
|
old := c.Set("k", 20)
|
||||||
|
if old != 10 {
|
||||||
|
t.Errorf("Set 返回旧值 = %d, want 10", old)
|
||||||
|
}
|
||||||
|
if got := c.Get("k"); got != 20 {
|
||||||
|
t.Errorf("Set 后 Get = %d, want 20", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMissing(t *testing.T) {
|
||||||
|
var c Counter
|
||||||
|
c.Init()
|
||||||
|
if got := c.Get("not-exist"); got != 0 {
|
||||||
|
t.Errorf("不存在的 key Get = %d, want 0", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeysIndependent(t *testing.T) {
|
||||||
|
var c Counter
|
||||||
|
c.Init()
|
||||||
|
c.Incr("a", 1)
|
||||||
|
c.Incr("b", 2)
|
||||||
|
if c.Get("a") != 1 || c.Get("b") != 2 {
|
||||||
|
t.Errorf("不同 key 应相互独立: a=%d b=%d", c.Get("a"), c.Get("b"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// counter 包示例:线程安全的键值计数器(并发场景下按 key 累加/读取/覆盖)
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.zeroonesoft.cn/golib/zogo/counter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var counters counter.Counter
|
||||||
|
counters.Init()
|
||||||
|
|
||||||
|
// 并发安全计数
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
counters.Incr("pv", 1)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
fmt.Println("并发 100 次 Incr 后 pv:", counters.Get("pv"))
|
||||||
|
|
||||||
|
// Set 覆盖并返回旧值
|
||||||
|
old := counters.Set("pv", 0)
|
||||||
|
fmt.Println("Set 返回旧值:", old, "新值:", counters.Get("pv"))
|
||||||
|
|
||||||
|
// 不同 key 相互独立
|
||||||
|
counters.Incr("uv", 1)
|
||||||
|
fmt.Println("uv:", counters.Get("uv"))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user