# sync 包扩展
我们使用泛型对 sync 包中的一些类型进行了封装。使用泛型的封装相比原来会有额外的性能损耗,目前通过基准测试我们确认,大概会多 3ns 左右的损耗,并且不会引入额外的内存分配。
你需要引入包来使用这些封装后的结构体:
import (
"github.com/gotomicro/ekit/syncx"
)
1
2
3
2
3
# 泛型 Pool
我们使用泛型了封装了 sync.Pool:
func ExampleNew() {
p := syncx.NewPool[[]byte](func() []byte {
res := make([]byte, 1, 12)
res[0] = 'A'
return res
})
res := p.Get()
fmt.Print(string(res))
// Output:
// A
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
注意这里我们要求 NewPool 里面传入的构造函数,不能为 nil。换言之,在 Pool 里面的所有元素都不能为 nil。
# 泛型 Map
类似地,我们也封装了 sync.Map:
func ExampleMap_Load() {
var m syncx.Map[string, int]
m.Store("key1", 123)
val, ok := m.Load("key1")
if ok {
fmt.Println(val)
}
// Output:
// 123
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
注意,key 只能是 comparable 类型。
# 分段锁
分段锁是用来解决这样一类场景:你需要对某些业务 Key 加锁,这个业务 Key 可能是你的业务 ID,或者唯一索引之类的。
但是如果你为每一个 Key 定义个 Mutex,那么可能会有非常多的 Mutex;同时你也希望,一个 Key 的 Mutex 用完之后,可以尝试释放掉它。
那么我们就提供了这样一个工具:SegmentKeysLock
。
这是一个简单的例子:
package syncx_test
import (
"fmt"
"github.com/ecodeclub/ekit/syncx"
)
func ExampleNewSegmentKeysLock() {
// 参数就是分多少段,你也可以理解为总共有多少锁
// 锁越多,并发竞争越低,但是消耗内存;
// 锁越少,并发竞争越高,但是内存消耗少;
lock := syncx.NewSegmentKeysLock(100)
// 对应的还有 TryLock
// RLock 和 RUnlock
lock.Lock("key1")
defer lock.Unlock("key1")
fmt.Println("OK")
// Output:
// OK
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SegmentKeysLock
提供了读锁、写锁,用法和原生的 sync.RWMutex
类似。
# LimitPool
通过控制申请次数来控制内存使用量的 Pool 实现,只有在一些你需要使用 Pool,但是又需要控制整体内存使用量的场景下才有意义。
如下图:
func ExampleNewLimitPool() {
p := syncx.NewLimitPool(1, func() int {
return 123
})
val, ok := p.Get()
fmt.Println("第一次", val, ok)
val, ok = p.Get()
fmt.Println("第二次", val, ok)
p.Put(123)
val, ok = p.Get()
fmt.Println("第三次", val, ok)
// Output:
// 第一次 123 true
// 第二次 0 false
// 第三次 123 true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在第二次调用 Get 的时候,因为 Pool 里面只有一个元素,并且已经被取走了,所以会得到一个 true 作为返回值。
当然,即便是返回了 false,这种情况下你会得到对应类型的 0 值。
← SQL 增强 atomic 包扩展 →