上下文桥接:ThreadLocal
当协程在不同线程间跳转时,如果不做处理,原本绑定在线程上的 ThreadLocal 变量(如 UserID, TraceID)会立即丢失。
场景再现
kotlin
val threadLocal = ThreadLocal<String>()
threadLocal.set("UserA")
launch(Dispatchers.IO) {
// 💥 线程切换了!这里大概率是 null,因为我们到了一个新的 Worker 线程
println(threadLocal.get())
}解决方案:asContextElement
协程框架提供了一个桥接器,在每次线程切换(恢复)时,自动帮你把数据“搬运”到新线程。
kotlin
val context = threadLocal.asContextElement(value = "UserA")
launch(Dispatchers.IO + context) {
// ✅ 协程框架自动在切换前设置了 ThreadLocal,执行后自动还原
println(threadLocal.get())
}MDC 集成 (日志追踪)
在后端或复杂 Android 日志中,常用 SLF4J MDC。
kotlin
// 定义一个 MDCContext 元素
class MDCContext(val map: Map<String, String> = MDC.getCopyOfContextMap())
: ThreadContextElement<Map<String, String>> {
companion object Key : CoroutineContext.Key<MDCContext>
override val key = Key
override fun updateThreadContext(context: CoroutineContext): Map<String, String> {
val oldState = MDC.getCopyOfContextMap()
MDC.setContextMap(map)
return oldState ?: emptyMap()
}
override fun restoreThreadContext(context: CoroutineContext, oldState: Map<String, String>) {
MDC.setContextMap(oldState)
}
}