You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Go并发安全的map/slice以更高效管理独占式资源?

实现方案说明

你提到的用atomic.CompareAndSwapInt64优化的思路是完全可行的,甚至不需要你说的「找到资源再加锁」,靠原子操作就能实现无锁的资源抢占,完全避免全局锁的性能开销,也能天然解决并发抢占同一资源的冲突问题。不过你给出的参考代码有两处明显错误:

  1. CompareAndSwapInt64 方法需要3个入参:变量指针、预期旧值、要替换的新值,你漏了旧值0的入参
  2. 代码里有拼写错误:Resouce应为Resource,同时遍历的应该是接收器m的成员变量,而不是全局的Resources

修正后的完整实现

import (
    "errors"
    "sync/atomic"
)

type Resource struct {
    InUse int64  // 0代表未占用,1代表已占用,所有读写必须用原子操作
    Path  string // 资源路径,例如/dev/chardeviceXXX
}

type ResourceManager struct {
    Resources []*Resource
}

// GetUnusedResource 抢占空闲资源
func (m *ResourceManager) GetUnusedResource() (*Resource, error) {
    for _, res := range m.Resources {
        // 原子操作:只有当前InUse为0时才会修改为1,返回true代表抢占成功
        if atomic.CompareAndSwapInt64(&res.InUse, 0, 1) {
            return res, nil
        }
    }
    return nil, errors.New("all resources are in use")
}

// ReleaseResource 释放占用的资源
func (m *ResourceManager) ReleaseResource(res *Resource) error {
    // 可选校验:判断资源是否属于当前管理器,避免传入非法资源
    if atomic.LoadInt64(&res.InUse) == 0 {
        return errors.New("resource is already released")
    }
    atomic.StoreInt64(&res.InUse, 0)
    return nil
}

这个实现的优势是完全没有全局锁,多个goroutine可以同时遍历资源列表抢占,CAS原子性保证了同一资源只会被一个goroutine抢占成功,冲突的goroutine会自动跳过继续查找下一个资源,在资源数量较多的场景下,并发性能远高于全局锁方案。


单元测试方案

核心测试场景覆盖如下:

  • 基础功能校验:单goroutine下申请、释放资源正常,资源占满后返回预期错误
  • 无超发校验:启动远多于资源数的goroutine同时抢占资源,验证同一时间最多和资源总数相等的goroutine能拿到资源
  • 并发释放校验:占满资源后随机释放部分资源,验证等待的goroutine可以正常抢到释放的资源
  • 异常操作校验:重复释放同一个资源返回预期错误,不会破坏资源状态
  • 稳定性校验:长时间跑数百万次申请、释放循环,验证无panic、无死锁、无状态异常

测试代码示例

import (
    "sync"
    "sync/atomic"
    "testing"
)

// 测试资源不会超发
func TestResourceNoOverIssue(t *testing.T) {
    // 初始化10个测试资源
    mgr := &ResourceManager{
        Resources: make([]*Resource, 10),
    }
    for i := range mgr.Resources {
        mgr.Resources[i] = &Resource{InUse: 0}
    }

    const goroutineNum = 100
    var acquireCount int32
    var wg sync.WaitGroup
    wg.Add(goroutineNum)

    // 启动100个goroutine同时抢资源,且不释放
    for i := 0; i < goroutineNum; i++ {
        go func() {
            defer wg.Done()
            _, err := mgr.GetUnusedResource()
            if err == nil {
                atomic.AddInt32(&acquireCount, 1)
            }
        }()
    }
    wg.Wait()

    if atomic.LoadInt32(&acquireCount) != 10 {
        t.Fatalf("resource over issue, expected max 10 acquired, got %d", acquireCount)
    }
}

// 测试并发申请释放的稳定性
func TestConcurrentAcquireRelease(t *testing.T) {
    mgr := &ResourceManager{
        Resources: make([]*Resource, 10),
    }
    for i := range mgr.Resources {
        mgr.Resources[i] = &Resource{InUse: 0}
    }

    const goroutineNum = 100
    const loopCount = 10000
    var wg sync.WaitGroup
    wg.Add(goroutineNum)

    for i := 0; i < goroutineNum; i++ {
        go func() {
            defer wg.Done()
            for j := 0; j < loopCount; j++ {
                res, err := mgr.GetUnusedResource()
                if err == nil {
                    // 模拟业务占用资源的耗时
                    _ = mgr.ReleaseResource(res)
                }
            }
        }()
    }
    wg.Wait()

    // 验证所有资源最终都被释放
    for _, res := range mgr.Resources {
        if atomic.LoadInt64(&res.InUse) != 0 {
            t.Fatal("resource leak found, not all resources are released")
        }
    }
}

内容的提问来源于stack exchange,提问作者Liam Kelly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 02:09:00