go-study/lock_free/delay_queue.go
fantasticbin 5b48ea1a62 使用codex优化代码,具体如下:
主要优化

  - err_group:去掉多余 goroutine,避免潜在泄漏;并把并发 append 改为按下标写
    入,消除数据竞争。
  - err_group 测试稳定性增强:放宽超时并增加结果长度断言。
  - semaphore:修复等待队列元素类型断言错误(*waiter);补充非法参数校验(负数
    acquire/release)。
  - SemaChan:修复 Lock/Unlock 逻辑(初始化令牌桶),避免永久阻塞。
  - observer:修复“每次 Notify 都启动新 fanout 协程”的问题:改为 sync.Once 只启动一次
    fanOut。
  - observer:修复并发读写观察者列表问题:给 Attach/Detach/fanOut 增加读写锁保护。
  - observer:去掉 fanout 内部额外再起 goroutine和自动关闭所有 observer 的行为,避
    免重复关闭/竞态风险(仍保留 Detach 时关闭单个 observer)。
  - lock_free:修复可取消延迟队列的计数错误与 timers map 并发访问问题。
  - lock_free:checkAckStatus 改为非阻塞读取,避免入队路径被卡住。
  - routine:提供默认空任务并忽略 nil taskFn,防止空指针调用。
  - ticker:发送改为非阻塞,Stop 幂等化,降低阻塞和重复关闭风险。
  - query_builder:WaitAndGo 增加 goroutine 内 panic 转 error;测试里
    的 GORM filter 链式写法修正。

  新增测试

  - 新增 semaphore 测试,覆盖 Acquire/Release/TryAcquire 与 SemaChan 并发上限。
2026-03-05 21:53:11 +08:00

104 lines
2.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package lock_free
import (
"iter"
"sync"
"sync/atomic"
"time"
)
type DelayLkQueue[TKey comparable, TValue any] struct {
timers map[TKey]*time.Timer
delayCount atomic.Uint64 // 用于统计延时队列数量
m sync.Mutex
LkQueue[TValue]
}
// NewDelayLkQueue 创建延迟无锁队列
func NewDelayLkQueue[TKey comparable, TValue any]() *DelayLkQueue[TKey, TValue] {
return &DelayLkQueue[TKey, TValue]{
make(map[TKey]*time.Timer),
atomic.Uint64{},
sync.Mutex{},
*NewLkQueue[TValue](),
}
}
// DelayCount 获取延时队列数量
func (q *DelayLkQueue[TKey, TValue]) DelayCount() uint64 {
return q.delayCount.Load()
}
// DelayEnqueue 延迟入队
func (q *DelayLkQueue[TKey, TValue]) DelayEnqueue(value TValue, duration time.Duration) {
q.delayCount.Add(1)
time.AfterFunc(duration, func() {
q.delayCount.Add(^uint64(0))
q.Enqueue(value)
})
}
// CancellableDelayEnqueue 可取消的延迟入队
func (q *DelayLkQueue[TKey, TValue]) CancellableDelayEnqueue(key TKey, value TValue, duration time.Duration) {
q.m.Lock()
defer q.m.Unlock()
if timer, ok := q.timers[key]; ok {
if timer.Stop() {
q.delayCount.Add(^uint64(0))
}
}
q.delayCount.Add(1)
q.timers[key] = time.AfterFunc(duration, func() {
q.delayCount.Add(^uint64(0))
q.m.Lock()
delete(q.timers, key)
q.m.Unlock()
q.Enqueue(value)
})
}
// CancelDelayEnqueue 取消延迟入队
func (q *DelayLkQueue[TKey, TValue]) CancelDelayEnqueue(key TKey) {
q.m.Lock()
defer q.m.Unlock()
if timer, ok := q.timers[key]; ok {
if timer.Stop() {
q.delayCount.Add(^uint64(0))
}
delete(q.timers, key)
}
}
// ContinuousDequeue 持续监听出队
func (q *DelayLkQueue[TKey, TValue]) ContinuousDequeue() iter.Seq[TValue] {
return func(yield func(TValue) bool) {
for {
if value, ok := q.Dequeue(); ok {
if !yield(value) {
return
}
} else {
time.Sleep(time.Millisecond) // 队列为空休眠1毫秒
}
}
}
}
// ContinuousDequeueExecute 持续监听出队执行函数
func (q *DelayLkQueue[TKey, TValue]) ContinuousDequeueExecute(fn func(TValue)) {
for value := range q.ContinuousDequeue() {
fn(value)
}
}
// ContinuousDequeueNotify 持续监听出队通知
func (q *DelayLkQueue[TKey, TValue]) ContinuousDequeueNotify(chs ...chan TValue) {
q.ContinuousDequeueExecute(func(value TValue) {
for _, ch := range chs {
ch <- value
}
})
}