Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Why give up using the cooperative program in Kotlin

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly explains why you give up using the cooperative program in Kotlin. The content of the explanation in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn why you give up the cooperative program in Kotlin.

Debug

Please look at the following code.

Suspend fun retrieveData (): SomeData {val request = createRequest () val response = remoteCall (request) return postProcess (response)} private suspend fun remoteCall (request: Request): Response {/ / do suspending REST call}

Suppose we want to debug the retrieveData function by placing a breakpoint in the first line. Then start the debugger (I'm using IntelliJ), and it stops at the breakpoint. Now let's execute a Step Over (skip calling createRequest), which is normal. But if you Step Over again, the program will run directly and will not stop after calling remoteCall ().

Why is this happening? the JVM debugger is bound to a Thread object. Of course, this is a very reasonable choice. However, when a co-program is introduced, a thread no longer does a thing. Take a closer look: remoteCall (request) calls a suspend function, although we don't see it in the syntax when we call it. So what happens? We execute the debugger "step over", which runs remoteCall's code and waits.

This is the difficulty: the current thread (to which our debugger is bound) is just the executor of our coroutine. What happens when we call the suspend function is that at some point the suspend function will yield. This means that another Thread will continue to execute our method. We effectively deceived the debugger.

The only solution I found was to place a breakpoint on the line I wanted to execute instead of using Step Over. Needless to say, this is a big problem. And obviously, it's not just about me.

In addition, in general debugging, it is difficult to determine what a single coroutine is currently doing because it jumps between threads. Of course, coroutine has a name, and you can print not only threads but also coroutine names in the log, but in my experience, the mental burden of debugging coroutine-based code is much higher than thread-based code.

Binding context data in REST call

A common design pattern for development on microservices is to receive a REST call with some form of authentication and pass the same authentication to all internal calls to other microservices. In the simplest case, we should at least keep the user name of the caller.

However, what if these calls to other microservices are nested 10 layers deep in our call stack? We certainly don't want to pass an authentication object as an argument in each function. We need some form of "context", this kind of context is hidden.

In traditional thread-based frameworks, such as Spring, the solution to this problem is to use ThreadLocal objects. This allows us to bind any kind of data to the current thread. As long as a thread corresponds to a REST call (you should always target this), this is exactly what we need. A good example of this pattern is Spring's SecurityContextHolder.

For coroutine, the situation is different. A ThreadLocal no longer corresponds to a co-program because your workload jumps from one thread to another; it is no longer a thread that accompanies a request throughout its life cycle. In Kotlin coroutine, there is CoroutineContext. In essence, it is just a HashMap that is carried with coroutine (no matter which thread it runs on). It has a terrible overdesigned API, which is troublesome to use, but that's not the main problem here.

The real problem is that coroutine does not automatically inherit context.

For example:

Suspend fun sum (): Int {val jobs = mutableListOf () for (child in children) {jobs + = async {/ / we lose our context here! Child.evaluate ()} return jobs.awaitAll (). Sum ()}

Whenever you call a coroutine builder, such as async, runBlocking, or launch, you will (by default) lose your current coroutine context. You can avoid this by explicitly passing the context to the builder method, but God bless you, don't forget to do so (the compiler doesn't care!).

A child coroutine can start with an empty context, and if a request for a context element comes in but doesn't find anything, you can request that element from the parent coroutine context. However, this doesn't happen in Kotlin, and developers need to do it manually, every time.

If you are interested in the details of this question, I suggest you read this blog post.

Https://blog.tpersson.io/2018/04/22/emulating-request-scoped-objects-with-kotlin-coroutines/

Synchronized no longer works as you think.

When dealing with locks or synchronized synchronous blocks in Java, the semantics I usually consider is "when I execute in this block, other calls cannot enter." Of course, "other calls" means that there is some kind of identity, in this case the thread, which should raise a big red warning signal in your mind.

Look at the following example.

Val lock = ReentrantLock () suspend fun doWithLock () {lock.withLock {callSuspendingFunction ()}}

This call is dangerous, and even if callSuspendingFunction () doesn't do anything dangerous, the code won't work as you might think.

Enter the synchronization lock

Call the suspend function

Yield, the current thread still holds the lock.

Another thread continues our coroutine

It's still the same journey, but we're not locked owner anymore!

The number of potential conflicts, deadlocks or other unsafe situations is staggering. You might say, we just need to design our code to deal with this problem. I agree, but we are talking about JVM. There is a huge Java library ecology. And they are not ready to deal with these situations.

The result here is that when you start using coroutine, you give up the possibility of using many Java libraries because they currently only work in thread-based environments.

Single machine throughput and horizontal expansion

One of the advantages of coroutine on the server side is that one thread can handle more requests; while one request waits for a database response, the same thread can happily serve another request. This can improve throughput, especially for Imap O-intensive tasks.

However, as this blog post hopes to show you, using coroutine has a non-zero cost overhead on many levels.

The question that arises is: is the benefit worth the cost? In my opinion, the answer is no. In cloud and microservice environments, there are some ready-made extension mechanisms, whether it's Google AppEngine, AWS Beanstalk, or some form of Kubernetes. If the current load increases, these technologies will simply generate new instances of your microservices on demand. Therefore, given the additional cost of introducing coroutine, the throughput that a single instance can handle is less important. This reduces the value we get from using coroutine.

Coroutine has its existence value.

Then again, Coroutine still has its usage scenarios. When developing a client-side UI with only one UI thread, coroutine can help improve your code structure while meeting the requirements of the UI framework. I hear this works well on Android. Coroutine is an interesting topic, but for server-side development, I think the collaborative process is almost interesting. The JVM development team is currently developing Fiber, which is essentially coroutine, but their goal is to better co-exist with the JVM base library. It will be interesting to see how it develops in the future and how Jetbrains will react to Kotlin coroutine. At best, Kotlin coroutine will simply map to Fiber in the future, and the debugger will be smart enough to handle them correctly.

Thank you for your reading, the above is the content of "Why give up the use of the cooperative program in Kotlin". After the study of this article, I believe you have a deeper understanding of why you give up using the cooperative program in Kotlin, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report