如何在Kotlin协程中使用依赖ThreadLocal的代码 [英] How to use code that relies on ThreadLocal with Kotlin coroutines

查看:636
本文介绍了如何在Kotlin协程中使用依赖ThreadLocal的代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

某些JVM框架使用ThreadLocal存储应用程序的调用上下文,例如 SLF4j MDC ,事务管理器,安全管理器等.

Some JVM frameworks use ThreadLocal to store the call context of a application, like the SLF4j MDC, transaction managers, security managers, and others.

但是,Kotlin协程是在不同的线程上调度的,那么如何使它工作呢?

However, Kotlin coroutines are dispatched on different threads, so how it can be made to work?

(该问题的灵感来自 GitHub问题)

推荐答案

协程与ThreadLocal的类似物是

Coroutine's analog to ThreadLocal is CoroutineContext.

要与使用库的ThreadLocal互操作,您需要实现自定义

To interoperate with ThreadLocal-using libraries you need to implement a custom ContinuationInterceptor that supports framework-specific thread-locals.

这里是一个例子.让我们假设我们使用依赖于特定ThreadLocal的某些框架来存储一些特定于应用程序的数据(在本示例中为MyData):

Here is an example. Let us assume that we use some framework that relies on a specific ThreadLocal to store some application-specific data (MyData in this example):

val myThreadLocal = ThreadLocal<MyData>()

要将其与协程一起使用,您需要实现一个上下文,该上下文保留MyData的当前值,并在每次在线程上恢复协程时将其放入相应的ThreadLocal中.代码应如下所示:

To use it with coroutines, you'll need to implement a context that keeps the current value of MyData and puts it into the corresponding ThreadLocal every time the coroutine is resumed on a thread. The code should look like this:

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) }
    }
}

要在协程中使用它,请包装要与MyContext一起使用的调度程序,并为其提供数据的初始值.该值将放入协程恢复所在线程的本地线程中.

To use it in your coroutines, you wrap the dispatcher that you want to use with MyContext and give it the initial value of your data. This value will be put into the thread-local on the thread where the coroutine is resumed.

launch(MyContext(MyData(), CommonPool)) {
    // do something...
}

上面的实现还可以跟踪对已完成的线程本地所做的任何更改并将其存储在此上下文中,因此,这种多次调用可以通过上下文共享线程本地"数据.

The implementation above would also track any changes to the thread-local that was done and store it in this context, so this way multiple invocation can share "thread-local" data via context.

更新:从kotlinx.corutines版本0.25.0开始,直接支持将Java ThreadLocal实例表示为协程上下文元素.参见此文档了解详情.通过kotlinx-coroutines-slf4j集成模块还提供了对SLF4J MDC的现成支持.

UPDATE: Starting with kotlinx.corutines version 0.25.0 there is direct support for representing Java ThreadLocal instances as coroutine context elements. See this documentation for details. There is also out-of-the-box support for SLF4J MDC via kotlinx-coroutines-slf4j integration module.

这篇关于如何在Kotlin协程中使用依赖ThreadLocal的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆