Cómo usar código que se basa en ThreadLocal con corotines de Kotlin
Algunos frameworks JVM usan ThreadLocal
para almacenar el context de llamada de una aplicación, como SLF4j MDC , gestores de transactions, administradores de security y otros.
Sin embargo, las corotinas de Kotlin se distribuyen en diferentes hilos, entonces, ¿cómo se puede hacer para que funcione?
- ¿Cómo puedo conectar un productor a un actor?
- Kotlin coroutines unit testing usando mockito
- ¿Cuál es la diferencia entre CoroutineContext y Job en kotlinx.coroutines?
- Kotlin coroutines y Spring Framework 5 types reactivos
- cómo implementar una llamada limitada con retrofit que bloquea el cliente y corutinas
(La pregunta está inspirada en el problema de GitHub )
- Variables "coroutine local" en kotlin
- ¿Pueden `SendChannel.offer`,` CompletableDefernetworking.complete` y similares llamarse corutinas externas?
- ¿Por qué hay una diferencia entre los constructores de coroutine para CompletableFuture y ListenableFuture?
- Qué significa cerrar un canal de kotlinx.coroutines
- ¿Por qué es imposible utilizar las references de methods para las funciones `suspender` en Kotlin?
- Corotines de Kotlin: ajuste el uso sincrónico de cassandra frente a la traducción del uso asynchronous
- Unidad probando una corotina de Kotlin con retraso
- Spring 5 and Kotlin 1.1 Coroutines: Type rx.Scheduler no presente
El análogo de ThreadLocal
a ThreadLocal
es CoroutineContext
.
Para interactuar con ThreadLocal
-utilizando librerías-, necesita implementar un ContinuationInterceptor
personalizado que sea compatible con thread-specific thread-locals.
Aquí hay un ejemplo. Supongamos que utilizamos algún marco que se basa en un ThreadLocal
específico para almacenar algunos datos específicos de la aplicación ( MyData
en este ejemplo):
val myThreadLocal = ThreadLocal<MyData>()
Para usarlo con corutinas, deberá implementar un context que mantenga el valor actual de MyData
y lo MyData
en el ThreadLocal
correspondiente cada vez que la corutina se reanude en un hilo. El código debería verse así:
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) } } }
Para usarlo en sus corotines, ajuste el despachador que desea usar con MyContext
y MyContext
el valor inicial de sus datos. Este valor se colocará en el thread-local en el hilo donde se reanuda la coroutine.
launch(MyContext(MyData(), CommonPool)) { // do something... }
La implementación anterior también haría un seguimiento de los cambios en el hilo local que se realizó y lo almacenará en este context, por lo que de esta manera, la invocación múltiple puede compartir datos "thread-local" a través del context.
- Moshi + Kotlin + SealedClass
- ¿Cuál es la mejor manera de definir la constante de logging de TAG en Kotlin?