Kotlin协程代码中async块未持有Mutex的含义、原因及意义是什么
Kotlin
CacheOnSuccess.kt 代码问题解答 注释的具体含义
该注释明确说明:async代码块内部执行block()时,之前通过mutex.withLock获取的互斥锁已经被释放,这段异步执行的逻辑不会持有Mutex锁。
为什么async块中不会持有mutex
协程Mutex的withLock方法自带自动释放逻辑:进入withLock代码块前会自动加锁,只要离开代码块(不管是正常返回还是抛出异常)就会自动解锁。
代码中async { }只是在withLock代码块内部创建了一个异步任务,不会等待异步任务执行完成,withLock代码块拿到async返回的Deferred实例后就直接退出,锁也随之释放。等后续调度执行async内部的block()时,锁已经被释放很久,自然不会持有mutex。
补充说明:给deferred赋值的also代码块是在withLock内部执行的,此时仍然持有锁,符合代码开头注释「所有deferred的读写操作都要持有锁保证线程安全」的要求。
这一设计的意义
- 提升并发性能:
block()通常是耗时操作(例如网络请求、数据库查询、 heavy计算),如果持有锁直到block()执行完才释放,这段时间所有调用getOrAwait()的协程都会被阻塞在抢锁步骤,完全无法并发,性能损失极大。该设计只把锁的持有范围限制在「检查deferred是否存在、创建新Deferred、赋值deferred」这几个极快的操作上,锁粒度极小,并发性能更高。 - 避免死锁风险:如果
block()内部存在其他加锁逻辑,长时间持有锁很容易出现不同协程交叉持有锁的死锁问题,锁早释放可以规避这类风险。 - 不影响功能正确性:加锁的核心目的只是保证「同一时间只有一个协程能创建Deferred实例,避免多个协程重复执行
block()」,只要创建Deferred和赋值deferred的操作在锁保护下完成即可。后续其他协程调用getOrAwait()抢锁时,会读到已经存在的deferred,直接返回同一个实例等待结果,不会重复触发block()执行,功能完全符合预期。
内容的提问来源于stack exchange,提问作者VIN
相关产品推荐
相关产品推荐

