go-study/cyclic_barrier/h2o.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

97 lines
2.2 KiB
Go

package cyclic_barrier
import (
"context"
"fmt"
"github.com/marusama/cyclicbarrier"
"golang.org/x/sync/semaphore"
"math/rand"
"sync"
"time"
)
const (
H2OHydrogenNum = 2 // 氢原子数量
H2OOxygenNum = 1 // 氧原子数量
)
// H2O 水分子结构体
type H2O struct {
semaH *semaphore.Weighted
semaO *semaphore.Weighted
cb cyclicbarrier.CyclicBarrier
}
func NewH2O() *H2O {
return &H2O{
semaH: semaphore.NewWeighted(H2OHydrogenNum), // 氢原子的信号量
semaO: semaphore.NewWeighted(H2OOxygenNum), // 氧原子的信号量
cb: cyclicbarrier.New(H2OHydrogenNum + H2OOxygenNum), // 循环栅栏,用来控制合成
}
}
func (h *H2O) hydrogen(releaseHydrogen func()) {
if err := h.semaH.Acquire(context.Background(), 1); err != nil { // 占用氢原子空槽
fmt.Println("Hydrogen Acquire err:", err)
}
releaseHydrogen()
if err := h.cb.Await(context.Background()); err != nil { // 等待栅栏放行
fmt.Println("Hydrogen Await err:", err)
}
h.semaH.Release(1) // 释放氢原子空槽
}
func (h *H2O) oxygen(releaseOxygen func()) {
if err := h.semaO.Acquire(context.Background(), 1); err != nil { // 占用氧原子空槽
fmt.Println("Oxygen Acquire err:", err)
}
releaseOxygen()
if err := h.cb.Await(context.Background()); err != nil { // 等待栅栏放行
fmt.Println("Oxygen Await err:", err)
}
h.semaO.Release(1) // 释放氧原子空槽
}
func (h *H2O) Gen(num uint) <-chan string {
// 计算总共需要生成的原子数量
sum := H2OHydrogenNum + H2OOxygenNum
numInt := int(num)
// 用来存放生成的原子
ch := make(chan string, numInt*sum)
releaseHydrogen := func() {
ch <- "H"
}
releaseOxygen := func() {
ch <- "O"
}
var wg sync.WaitGroup
// 使用 WaitGroup 等待所有的 goroutine 完成
wg.Add(numInt * sum)
for i := 0; i < numInt*H2OHydrogenNum; i++ {
go func() {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
h.hydrogen(releaseHydrogen)
wg.Done()
}()
}
for i := 0; i < numInt*H2OOxygenNum; i++ {
go func() {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
h.oxygen(releaseOxygen)
wg.Done()
}()
}
go func() {
wg.Wait()
close(ch)
}()
return ch
}