简体   繁体   中英

“coroutine local” variables in kotlin

Java has ThreadLocal variables that are nice for running parallel operations without stepping on other threads or per-loop allocations, for example OpenCV uses videoCapture.retrieve(image) , and "image" could be a threadlocal variable.

Does Kotlin have any sense of "coroutine-local" variables? If I wanted to take their counter example but have a counter per coroutine, how would I do that?

for (i in 1..1_000_000)
    thread(start = true) {
       c.addAndGet(i)
    }

If you are looking for ThreadLocal as a performance optimization, to make sure that each thread gets it own copy of some temporary object, then you should continue using ThreadLocal for that purpose. There can be much more coroutines than threads, and keeping a copy of some temporary object for each coroutine may do more harm than good.

If you are looking for ThreadLocal as a way to pass some context around method invocation, then I strongly suggest to consider explicitly passing this context into your functions or using some dependency injection framework to do that.

If you have a rare case where you indeed need to pass some context around, but for some technical reason you cannot pass it explicitly nor you can use DI (that is where you would have used ThreadLocal with threads), you can use CoroutineContext with coroutines. The steps are:

Define your own coroutine context element class using the following template:

class MyContextElement : AbstractCoroutineContextElement(MyContextElement) {
    companion object Key : CoroutineContext.Key<MyContextElement>
    // you state/code is here
}

Create an instance of your element and pass it to the coroutine builder when you start your coroutine. The following example uses launch coroutine builder, but it works with all of them ( async , produce , actor , etc)

launch(MyContextElement()) {
    // the code of your coroutine
}

You can combine your context with other context elements using + operators (see "Combining Contexts" in the guide for details)

From inside your coroutine code you can always retrieve your element from the coroutineContext . All the standard builders bring the CoroutineScope instance into their scope, which makes its coroutineContext property available. If you are deep in the call stack of suspending functions, then you can define your own coroutineContext() helper function to retrieve the current context until it makes its way into the standard library in one of the future updates. See KT-17609 for details.

With the coroutineScope in hand, it is easy to retrieve your element:

val myElement = coroutineScope[MyContextElement]

For those stumbling upon this question nowadays, it seems that the syntax has changed a bit:

class MyContextElement : AbstractCoroutineContextElement(MyContextElement), CoroutineContext.Element {
    override val key = Key

    companion object Key : CoroutineContext.Key<KCallScope>

}

In order to be CoroutineContext.Key you now need to implement CoroutineContext.Element , which requires you to implement key getter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM