Kotlin协程:如何检测是否已处于挂起上下文?
实现方案:禁止从挂起函数/协程调用含runBlocking的函数
核心思路是检测调用点是否处于非runBlocking的协程上下文,因为runBlocking本身会创建协程环境,但我们需要禁止的是在已有协程(如挂起函数、普通协程)中调用它,避免线程阻塞问题。
具体实现
1. 编写上下文检测函数
通过Dispatchers.Unconfined在当前线程立即执行协程,获取调用点的协程Job类型,判断是否为runBlocking专属的BlockingCoroutine:
import kotlinx.coroutines.* private fun isInvokedFromCoroutine(): Boolean { return runBlocking(Dispatchers.Unconfined) { val currentJob = coroutineContext[Job] ?: return@runBlocking false // 若当前Job不是runBlocking专属的BlockingCoroutine,说明调用点在其他协程环境 currentJob !is BlockingCoroutine } }
2. 改造目标函数
在函数开头加入检测逻辑,不符合条件则抛出异常,再执行原有的并行协程逻辑:
import kotlinx.coroutines.async fun parallelTaskRunner(): List<String> { // 检测调用上下文,若来自协程/挂起函数则抛出异常 if (isInvokedFromCoroutine()) { throw IllegalStateException("该函数不能从挂起函数或协程内部调用") } return runBlocking { // 并行启动协程并收集结果 val tasks = listOf( async { fetchData("任务1") }, async { fetchData("任务2") }, async { fetchData("任务3") } ) tasks.awaitAll() } } // 模拟数据获取的挂起函数 private suspend fun fetchData(taskName: String): String { delay(100) return "$taskName 的执行结果" }
测试场景
- 正常调用(普通函数中):
fun main() { val results = parallelTaskRunner() println(results) // 输出:[任务1 的执行结果, 任务2 的执行结果, 任务3 的执行结果] } - 异常调用(挂起函数中):
suspend fun caller() { parallelTaskRunner() // 此处会抛出IllegalStateException } fun testCaller() = runBlocking { caller() // 执行到此处触发异常 }
扩展:禁止所有协程环境调用
如果需要连runBlocking内部的嵌套调用也禁止,可以修改检测逻辑,只要存在协程Job就抛出异常:
private fun isInvokedFromAnyCoroutine(): Boolean { return runBlocking(Dispatchers.Unconfined) { coroutineContext[Job] != null } }
内容的提问来源于stack exchange,提问作者AlexC
相关产品推荐
相关产品推荐

