二次检查(Double Check),防止ArgumentException

一、ArgumentException

参数不合法,比如对一个字典重复的添加key,比如一个方法期望的参数是string,结果传来一个null。

二、需求场景

异步场景下对同一个 Dictionary 重复 Add 同一 key 的竞态问题与解决。

比如我有一个Dictionary<string,Pool> pools,存储对象池。

然后我需要对一个资源进行中GetOrCreatePool,pools有就直接返回,没有就创建后返回。

那么当2个异步S1和S2操作同时执行:

// 任意一个地方代码执行了
await S1();
// 同时另一个对象的一个代码执行了
await S2();
// 现在S1和S2两个异步同时开启

public async UniTask S1()
{
    await 加载资源A; // 耗时操作

    AddToDic();
}

public async UniTask  S2()
{
    await 加载资源A; // 耗时操作

    AddToDic();
}

private void AddToDic()
{
    // 向pool添加 ["A",资源A]
}

整个流程是:
S1方法开始运行。

S2方法开始运行。

S1加载资源A。

S2加载资源A。

S1加载完毕,向字典添加。

S2加载资源完毕,向字典添加。

此时发现,ArgumentException。

三、解决

加载资源前进行一次检查,如果有就不需要再加载资源了。

加载资源后进行一次检查,如果有就不创建池了。

public async UniTask<LeanGameObjectPool> GetOrCreatePoolAsync(
            string key,
            Func<UniTask<GameObject>> prefabLoader,
            string poolName = null,
            int preloadCount = 0,
            int capacity = 0,
            bool recycle = false)
        {
            Debug.Log("GetOrCreatePoolAsync Key:" + key);

            if (string.IsNullOrEmpty(key))
                throw new ArgumentException("Pool key is null or empty");

            // // 第一次快速检查
            if (_pools.TryGetValue(key, out var existingPool))
            {
                return existingPool;
            }

            // 真正去加载 Prefab(这一步可能会耗时,不能加锁)
            var prefab = await prefabLoader();
            if (prefab == null)
            {
                throw new NullReferenceException($"GetOrCreatePoolAsync prefab is null, key = {key}");
            }

            poolName ??= key;

            // 二次检查 + 创建 
            // 有可能别的协程在我们等待 prefab 的时候已经创建好这个 pool 了
            if (_pools.TryGetValue(key, out  existingPool))
            {
                return existingPool;
            }

            var pool = LeanPoolCreateUtils.CreateLeanPool(
                poolName,
                prefab,
                preloadCount,
                poolsRoot,
                capacity,
                recycle);

            _pools.Add(key, pool);
            return pool;
        }

这是一个简单的问题,但是是一个常犯的错误。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值