统计Room数据库待插入记录时pendingRecords最终不为零如何解决
问题原因
你遇到的pendingRecords最终不为0的问题,核心是以下几个错误:
- DAO的
insert方法没有声明为suspend,属于阻塞式调用,虽然你主动切了IO调度器,但Room原生支持协程的挂起操作,阻塞调用在高并发批量插入场景下更容易出现调度时序问题 pendingRecords的加减操作不是线程安全的:你在IO调度器的多线程并发环境下直接对普通变量执行++/--操作,该操作本身不是原子指令,多个协程同时操作时会出现写覆盖,导致计数丢失- 嵌套启动协程的逻辑冗余且容易出错:你在父协程内部又额外启动子协程执行插入,批量场景下会产生大量不必要的协程开销,同时如果父协程提前取消,部分子协程的
invokeOnCompletion可能没有被正确触发
最优解决方案
1. 优先使用批量插入(性能最高)
如果是批量插入场景,不要逐条启动协程插入,直接用Room的批量插入方法,单次数据库交互即可完成,计数逻辑完全可控:
首先修改DAO:
@Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertBatch(sensors: List<Pressure>)
然后修改插入逻辑:
viewModelScope.launch { // 批量插入前直接设置待处理总数 pendingRecords.set(sensorDataList.size) // Room的suspend方法自动切IO线程,不需要手动指定调度器 database?.pressureDao()?.insertBatch(sensorDataList) // 插入完成直接置0,不会有计数误差 pendingRecords.set(0) }
2. 如需逐条插入统计进度
如果确实需要逐条插入做进度统计,首先将pendingRecords替换为线程安全的原子类,避免并发计数错误:
// 声明原子计数类,替代原有的Int类型pendingRecords private val pendingRecords = AtomicInteger(0)
同时修改DAO的插入方法为suspend类型:
@Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(sensor: Pressure)
插入逻辑调整如下,不需要嵌套切线程,也不需要用invokeOnCompletion,直接用finally保证计数必减:
viewModelScope.launch { sensorDataList.forEach { sensorData -> pendingRecords.incrementAndGet() // 启动子协程执行单条插入 launch { try { database?.pressureDao()?.insert(sensorData) } finally { pendingRecords.decrementAndGet() } } } }
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

