如何防止Kotlin协程重复调用?实现同标签协程仅运行一个
Kotlin协程实现同标签互斥运行的方案
Kotlin协程本身没有原生支持“标签+同标签互斥”的API,但可以通过自定义扩展函数结合状态管理实现你要的效果。根据你期望的“同一时刻仅运行一个同标签协程,后续同标签协程直接忽略”的需求,以下是具体实现:
方案1:忽略后续同标签协程(匹配你的示例输出)
维护一个映射表记录每个标签当前活跃的协程Job,新的同标签协程启动时,先检查是否已有活跃的同标签协程,有则直接跳过启动:
import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock // 存储标签与对应活跃Job的映射,需保证线程安全 private val activeTagJobs = mutableMapOf<String, Job>() private val mapMutex = Mutex() // 保护映射表的线程安全操作 // 给CoroutineScope扩展自定义launch函数 fun CoroutineScope.launchSingleWithTag( context: CoroutineContext = EmptyCoroutineContext, tag: String, block: suspend CoroutineScope.() -> Unit ): Job? { return mapMutex.withLock { // 检查当前标签是否有活跃协程 val existingActiveJob = activeTagJobs[tag] if (existingActiveJob?.isActive == true) { // 已有活跃协程,返回null表示不启动新协程 return@withLock null } // 启动新协程并记录到映射表 val newJob = launch(context) { try { block() } finally { // 协程结束后移除标签记录 mapMutex.withLock { activeTagJobs.remove(tag) } } } activeTagJobs[tag] = newJob newJob } }
使用示例
// 建议用自定义Scope替代GlobalScope,避免内存泄漏 val appScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) appScope.launchSingleWithTag(Dispatchers.IO, "tag_a") { println("Hello World ${System.currentTimeMillis()}") delay(1000) // 模拟耗时任务 } // 此时第一个协程仍在运行,第二个同标签协程会被忽略 appScope.launchSingleWithTag(Dispatchers.IO, "tag_a") { println("Hello World ${System.currentTimeMillis()}") } // 输出结果仅一行: // Hello World 1718XXXXXXX
方案2:同标签协程串行执行
如果希望后续同标签协程不被忽略,而是等前面的执行完再运行,可以用Mutex实现串行化:
private val tagMutexMap = mutableMapOf<String, Mutex>() private val mutexMapLock = Mutex() fun CoroutineScope.launchSerialWithTag( context: CoroutineContext = EmptyCoroutineContext, tag: String, block: suspend CoroutineScope.() -> Unit ): Job { return launch(context) { // 获取或创建对应标签的Mutex val mutex = mutexMapLock.withLock { tagMutexMap.getOrPut(tag) { Mutex() } } // 加锁后执行协程体,确保同一时间仅一个同标签协程运行 mutex.withLock { block() } } }
注意事项
- 避免使用
GlobalScope,它的协程不受生命周期管理,易引发内存泄漏,建议使用和业务生命周期绑定的自定义CoroutineScope。 - 两种方案按需选择:需丢弃重复请求选方案1,需排队执行选方案2。
内容的提问来源于stack exchange,提问作者AlanSTACK
相关产品推荐
相关产品推荐

