Certaines infrastructures JVM utilisent ThreadLocal
pour stocker le contexte d'appel d'une application, comme le SLF4j MDC , les gestionnaires de transactions, les gestionnaires de sécurité et autres.
Cependant, les coroutines Kotlin sont réparties sur différents threads, alors comment peut-il fonctionner?
(La question est inspirée de problème GitHub )
L'analogue de Coroutine avec ThreadLocal
est CoroutineContext
.
Pour interagir avec ThreadLocal
- à l'aide de bibliothèques, vous devez implémenter un ContinuationInterceptor
personnalisé qui prend en charge les sections locales de thread spécifiques au framework.
Voici un exemple. Supposons que nous utilisons un framework qui repose sur un ThreadLocal
spécifique pour stocker des données spécifiques à l'application (MyData
dans cet exemple):
val myThreadLocal = ThreadLocal<MyData>()
Pour l'utiliser avec des coroutines, vous devrez implémenter un contexte qui conserve la valeur actuelle de MyData
et la place dans le ThreadLocal
correspondant à chaque reprise de la coroutine sur un thread. Le code devrait ressembler à ceci:
class MyContext(
private var myData: MyData,
private val dispatcher: ContinuationInterceptor
) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
dispatcher.interceptContinuation(Wrapper(continuation))
inner class Wrapper<T>(private val continuation: Continuation<T>): Continuation<T> {
private inline fun wrap(block: () -> Unit) {
try {
myThreadLocal.set(myData)
block()
} finally {
myData = myThreadLocal.get()
}
}
override val context: CoroutineContext get() = continuation.context
override fun resume(value: T) = wrap { continuation.resume(value) }
override fun resumeWithException(exception: Throwable) = wrap { continuation.resumeWithException(exception) }
}
}
Pour l'utiliser dans vos coroutines, vous encapsulez le répartiteur que vous souhaitez utiliser avec MyContext
et lui donnez la valeur initiale de vos données. Cette valeur sera placée dans le thread local sur le thread où la coroutine est reprise.
launch(MyContext(MyData(), CommonPool)) {
// do something...
}
L'implémentation ci-dessus permettrait également de suivre les modifications apportées au thread-local et de les stocker dans ce contexte, de cette façon, plusieurs invocations peuvent partager des données "thread-local" via le contexte.
[~ # ~] mise à jour [~ # ~] : à partir de kotlinx.corutines
version 0.25.0
il y a un support direct pour représenter les instances Java ThreadLocal
en tant qu'éléments de contexte coroutine. Voir cette documentation pour plus de détails. prise en charge de SLF4J MDC via kotlinx-coroutines-slf4j
module d'intégration.