syncx

package
v0.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2024 License: Apache-2.0 Imports: 5 Imported by: 1

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cond added in v0.0.8

type Cond struct {

	// L 在观察或改变条件时被加锁
	L sync.Locker
	// contains filtered or unexported fields
}

Cond 实现了一个条件变量,是等待或宣布一个事件发生的goroutines的汇合点。

在改变条件和调用Wait方法的时候,Cond 关联的锁对象 L (*Mutex 或者 *RWMutex)必须被加锁,

在Go内存模型的术语中,Cond 保证 Broadcast或Signal的调用 同步于 因此而解除阻塞的 Wait 之前。

绝大多数简单用例, 最好使用 channels 而不是 Cond (Broadcast 对应于关闭一个 channel, Signal 对应于给一个 channel 发送消息).

func NewCond added in v0.0.8

func NewCond(l sync.Locker) *Cond

NewCond 返回 关联了 l 的新 Cond .

func (*Cond) Broadcast added in v0.0.8

func (c *Cond) Broadcast()

Broadcast 唤醒所有等待在 c 上的goroutine.

调用时,caller 可以持有也可以不持有 c.L 锁

func (*Cond) Signal added in v0.0.8

func (c *Cond) Signal()

Signal 唤醒一个等待在 c 上的goroutine.

调用时,caller 可以持有也可以不持有 c.L 锁

Signal() 不影响 goroutine 调度的优先级; 如果其它的 goroutines 尝试着锁定 c.L, 它们可能在 "waiting" goroutine 之前被唤醒.

func (*Cond) Wait added in v0.0.8

func (c *Cond) Wait(ctx context.Context) error

Wait 自动解锁 c.L 并挂起当前调用的 goroutine. 在恢复执行之后 Wait 在返回前将加 c.L 锁成功. 和其它系统不一样, 除非调用 Broadcast 或 Signal 或者 ctx 超时了,否则 Wait 不会返回.

成功唤醒时, 返回 nil. 超时失败时, 返回ctx.Err(). 如果 ctx 超时了, Wait 可能依旧执行成功返回 nil.

在 Wait 第一次继续执行时,因为 c.L 没有加锁, 当 Wait 返回的时候,调用者通常不能假设条件是真的 相反, caller 应该在循环中调用 Wait:

	c.L.Lock()
	for !condition() {
	    if err := c.Wait(ctx); err != nil {
          // 超时唤醒了,并不是被正常唤醒的,可以做一些超时的处理
		}
	}
	... condition 满足了,do work ...
	c.L.Unlock()

type LimitPool added in v0.0.9

type LimitPool[T any] struct {
	// contains filtered or unexported fields
}

LimitPool 是对 Pool 的简单封装允许用户通过控制一段时间内对Pool的令牌申请次数来间接控制Pool中对象的内存总占用量

func NewLimitPool added in v0.0.9

func NewLimitPool[T any](maxTokens int, factory func() T) *LimitPool[T]

NewLimitPool 创建一个 LimitPool 实例 maxTokens 表示一段时间内的允许发放的最大令牌数 factory 必须返回 T 类型的值,并且不能返回 nil

Example
package main

import (
	"fmt"

	"github.com/ecodeclub/ekit/syncx"
)

func main() {
	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

func (*LimitPool[T]) Get added in v0.0.9

func (l *LimitPool[T]) Get() (T, bool)

Get 取出一个元素 如果返回值是 true,则代表确实从 Pool 里面取出来了一个 否则是新建了一个

func (*LimitPool[T]) Put added in v0.0.9

func (l *LimitPool[T]) Put(t T)

Put 放回去一个元素

type Map

type Map[K comparable, V any] struct {
	// contains filtered or unexported fields
}

Map 是对 sync.Map 的一个泛型封装 要注意,K 必须是 comparable 的,并且谨慎使用指针作为 K。 使用指针的情况下,两个 key 是否相等,仅仅取决于它们的地址 而不是地址指向的值。可以参考 Load 测试。 注意,key 不存在和 key 存在但是值恰好为零值(如 nil),是两码事

func (*Map[K, V]) Delete

func (m *Map[K, V]) Delete(key K)

Delete 删除键值对

Example
var m Map[string, int]
m.Store("key1", 123)
val, ok := m.Load("key1")
if ok {
	fmt.Printf("key1 = %d\n", val)
}
m.Delete("key1")
_, ok = m.Load("key1")
if !ok {
	fmt.Println("key1 已被删")
}
Output:

key1 = 123
key1 已被删

func (*Map[K, V]) Load

func (m *Map[K, V]) Load(key K) (value V, ok bool)

Load 加载键值对

Example
var m Map[string, int]
m.Store("key1", 123)
val, ok := m.Load("key1")
if ok {
	fmt.Println(val)
}
Output:

123

func (*Map[K, V]) LoadAndDelete

func (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool)

LoadAndDelete 加载并且删除一个键值对

Example
var m = Map[string, *User]{}
_, loaded := m.LoadAndDelete("Tom")
fmt.Println(loaded)
m.Store("Tom", nil)
val, loaded := m.LoadAndDelete("Tom")
if loaded {
	fmt.Printf("key=Tom, val=%v 被删除\n", val)
}
m.Store("Tom", &User{Name: "Tom"})
val, loaded = m.LoadAndDelete("Tom")
if loaded {
	fmt.Printf("key=Tom, val=%v 被删除\n", val)
}
Output:

false
key=Tom, val=<nil> 被删除
key=Tom, val=&{Tom} 被删除

func (*Map[K, V]) LoadOrStore

func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool)

LoadOrStore 加载或者存储一个键值对 true 代表是加载的,false 代表执行了 store

Example
var m = Map[string, *User]{}
_, loaded := m.LoadOrStore("Tom", &User{Name: "Tom"})
// 执行存储
if !loaded {
	fmt.Println("设置了新值 Tom")
}

_, loaded = m.LoadOrStore("Tom", &User{Name: "Tom-copy"})
// Tom 这个 key 已经存在,执行加载
if loaded {
	fmt.Println("加载旧值 Tom")
}

_, loaded = m.LoadOrStore("Jerry", nil)
// 执行存储,注意值是 nil
if !loaded {
	fmt.Println("设置了新值 nil")
}
val, loaded := m.LoadOrStore("Jerry", &User{Name: "Jerry"})
// Jerry 这个 key 已经存在,执行加载,于是把原本的 nil 加载出来
if loaded {
	fmt.Printf("加载旧值 %v", val)
}
Output:

设置了新值 Tom
加载旧值 Tom
设置了新值 nil
加载旧值 <nil>

func (*Map[K, V]) LoadOrStoreFunc added in v0.0.8

func (m *Map[K, V]) LoadOrStoreFunc(key K, fn func() (V, error)) (actual V, loaded bool, err error)

LoadOrStoreFunc 是一个优化,也就是使用该方法能够避免无意义的创建实例。 如果你的初始化过程非常消耗资源,那么使用这个方法是有价值的。 它的代价就是 Key 不存在的时候会多一次 Load 调用。 当 fn 返回 error 的时候,LoadOrStoreFunc 也会返回 error。

Example
var m = Map[string, *User]{}
_, loaded, _ := m.LoadOrStoreFunc("Tom", func() (*User, error) {
	return &User{Name: "Tom"}, nil
})
// 执行存储
if !loaded {
	fmt.Println("设置了新值 Tom")
}

_, loaded, _ = m.LoadOrStoreFunc("Tom", func() (*User, error) {
	return &User{Name: "Tom-copy"}, nil
})
// Tom 这个 key 已经存在,执行加载
if loaded {
	fmt.Println("加载旧值 Tom")
}

_, loaded, _ = m.LoadOrStoreFunc("Jerry", func() (*User, error) {
	return nil, nil
})
// 执行存储,注意值是 nil
if !loaded {
	fmt.Println("设置了新值 nil")
}
val, loaded, _ := m.LoadOrStoreFunc("Jerry", func() (*User, error) {
	return &User{Name: "Jerry"}, nil
})
// Jerry 这个 key 已经存在,执行加载,于是把原本的 nil 加载出来
if loaded {
	fmt.Printf("加载旧值 %v\n", val)
}

_, _, err := m.LoadOrStoreFunc("Kitty", func() (*User, error) {
	return nil, errors.New("初始化失败")
})
if err != nil {
	fmt.Println(err.Error())
}
Output:

设置了新值 Tom
加载旧值 Tom
设置了新值 nil
加载旧值 <nil>
初始化失败

func (*Map[K, V]) Range

func (m *Map[K, V]) Range(f func(key K, value V) bool)

Range 遍历, f 不能为 nil 传入 f 的时候,K 和 V 直接使用对应的类型,如果 f 返回 false,那么就会中断遍历

Example
var m Map[string, int]
m.Store("Tom", 18)
m.Store("Jerry", 35)
var sum int
m.Range(func(key string, val int) bool {
	sum += val
	return true
})
fmt.Println(sum)
Output:

53

func (*Map[K, V]) Store

func (m *Map[K, V]) Store(key K, value V)

Store 存储键值对

Example
var m Map[string, int]
m.Store("key1", 123)
val, ok := m.Load("key1")
if ok {
	fmt.Printf("key1 = %d\n", val)
}
Output:

key1 = 123

type Pool

type Pool[T any] struct {
	// contains filtered or unexported fields
}

Pool 是对 sync.Pool 的简单封装 会有一些性能损耗,但是基本可以忽略不计。担忧性能问题的可以参考

func NewPool

func NewPool[T any](factory func() T) *Pool[T]

NewPool 创建一个 Pool 实例 factory 必须返回 T 类型的值,并且不能返回 nil

func (*Pool[T]) Get

func (p *Pool[T]) Get() T

Get 取出一个元素

func (*Pool[T]) Put

func (p *Pool[T]) Put(t T)

Put 放回去一个元素

type SegmentKeysLock added in v0.0.9

type SegmentKeysLock struct {
	// contains filtered or unexported fields
}

SegmentKeysLock 部分key lock结构定义

func NewSegmentKeysLock added in v0.0.9

func NewSegmentKeysLock(size uint32) *SegmentKeysLock

NewSegmentKeysLock 创建 SegmentKeysLock 示例

Example
package main

import (
	"fmt"

	"github.com/ecodeclub/ekit/syncx"
)

func main() {
	// 参数就是分多少段,你也可以理解为总共有多少锁
	// 锁越多,并发竞争越低,但是消耗内存;
	// 锁越少,并发竞争越高,但是内存消耗少;
	lock := syncx.NewSegmentKeysLock(100)
	// 对应的还有 TryLock
	// RLock 和 RUnlock
	lock.Lock("key1")
	defer lock.Unlock("key1")
	fmt.Println("OK")
}
Output:

OK

func (*SegmentKeysLock) Lock added in v0.0.9

func (s *SegmentKeysLock) Lock(key string)

Lock 写锁加锁

func (*SegmentKeysLock) RLock added in v0.0.9

func (s *SegmentKeysLock) RLock(key string)

RLock 读锁加锁

func (*SegmentKeysLock) RUnlock added in v0.0.9

func (s *SegmentKeysLock) RUnlock(key string)

RUnlock 读锁解锁

func (*SegmentKeysLock) TryLock added in v0.0.9

func (s *SegmentKeysLock) TryLock(key string) bool

TryLock 试着加锁,加锁成功会返回 true

func (*SegmentKeysLock) TryRLock added in v0.0.9

func (s *SegmentKeysLock) TryRLock(key string) bool

TryRLock 试着加读锁,加锁成功会返回

func (*SegmentKeysLock) Unlock added in v0.0.9

func (s *SegmentKeysLock) Unlock(key string)

Unlock 写锁解锁

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL