异步协程锁¶
使用异步操作已成为现代开发中不可或缺的手段,但它也带来了某些潜在问题。由于异步操作可能导致逻辑执行缺乏原子性,如果处理不当,可能会引发一些意想不到的错误。为了应对这一挑战,框架提供了 CoroutineLock,用于确保异步操作的原子性。CoroutineLock 通过将异步逻辑按队列顺序执行,避免了因并发操作导致的竞争条件,从而有效解决了异步环境下的原子性问题。
在当前的异步操作流程中,任务将按照队列顺序依次执行逻辑,直至成功释放锁为止。在此期间,每个任务都将按其进入队列的顺序等待锁的解除,以确保资源的有序访问,从而保证线程的安全性和系统的稳定性。通过这种方式,我们能够有效地防止资源竞争,避免死锁等潜在问题的发生。
锁定CoroutineLock¶
要使用CoroutineLock,需要使用当前场景(Scene)中的 CoroutineLockComponent 组件。
我们框架中提供了两种创建 CoroutineLock 的方式,以适应不同的使用场景。
Wait¶
Wait 方法接受四个参数:
- coroutineLockType: 用于指定等待的锁类型。这个参数决定了当前协程需要等待哪一种锁,以确保多协程环境中的操作按预期顺序执行,防止数据竞争或死锁的发生。
- coroutineLockQueueKey: 用于指定等待的锁的唯一标识符。由于同一类型的锁可能存在多个实例,该参数明确了当前协程应等待的具体锁实例,以便进行精准的资源管理。
- time: 设置协程等待锁的最大时长,单位为毫秒,默认值为 30000 毫秒(即 30 秒)。如果在设定时间内未能成功获取锁,系统将记录一条 Error 级别的日志,提示可能存在的性能问题或死锁风险。。
- tag: 用于标识超时锁的具体标签。当锁超时未获取成功时,此参数会在日志中作为标识输出,帮助开发者快速定位并分析锁超时的原因。默认值为 null,可以根据实际需求自定义该标识。
Wait
Create¶
Wait 方法虽然使用起来简单快捷,但在性能方面表现欠佳,因为每次调用 Wait 方法都涉及两次字典查询操作。相比之下,使用 Create 方法可以避免这些查询,从而提高效率。因此,在对性能要求较高的场景中,Create 方法是一个更为优选的选择。
Create 方法接受一个参数:
- coroutineLockType:参数的作用与 Wait 方法中的相同,用于指定锁的类型。
调用 Create 方法后,将返回一个 CoroutineLock 实例。通过该实例,可以使用 CoroutineLock 类中的 Wait 方法来实现相应的功能。由于在调用 Create 方法时已经传递了 coroutineLockType 参数,因此在调用 CoroutineLock 的 Wait 方法时,无需再次传入 coroutineLockType。只需提供 coroutineLockQueueKey、time 和 tag 这三个参数即可。
Create
释放CoroutineLock¶
使用 CoroutineLock.Wait 方法后,需要手动释放 CoroutineLock。如果不及时释放,后续的操作将会一直处于等待状态,直到该锁被释放后才能继续执行。这可能导致整个流程的阻塞,因此在完成锁内操作后,务必立即释放锁,以确保程序的正常运行。
Dispose¶
在使用 Wait 方法后,将会返回一个 WaitCoroutineLock 对象。类似地,Create 方法也会通过 coroutineLock.Wait 返回一个 WaitCoroutineLock 实例。为了确保资源的正确释放并避免潜在的死锁问题,您可以通过调用 WaitCoroutineLock.Dispose 方法来释放该锁。
值得注意的是,WaitCoroutineLock 实现了 IDisposable 接口,这意味着您可以利用 using 语句来自动调用 Dispose 方法,从而简化资源管理并减少手动释放锁的操作风险。这不仅提高了代码的健壮性,还确保了在出现异常情况时资源能得到及时的释放。
Wait
Release¶
有时间可能拿不到waitCoroutineLock,但还需要根据coroutineLockType和coroutineLockQueueKey来释放锁,这种情况下可以使用Release方法。
Release 方法接受二个参数:
- coroutineLockType: 用于释放等待的锁类型。
- coroutineLockQueueKey: 用于释放等待的锁的唯一标识符。
CoroutineLock.Dispose¶
只有通过 Create 方法创建对象时,才会返回一个 CoroutineLock,并且可以通过调用 CoroutineLock.Dispose 方法来释放当前持有的所有锁。不过,需要特别注意的是,执行 Dispose 方法后,CoroutineLock 会被放回对象池中。如果程序中仍然有其他地方引用了这个 CoroutineLock,一定要及时清除这些引用,否则可能会导致潜在的问题或异常行为。