Kotlin 1.4.0-RC: Debugging coroutines

栏目: IT技术 · 发布时间: 4年前

内容简介:We continue to highlight the upcoming changes in 1.4 release. In this blogpost, we want to describe a couple of important features related to coroutines:These changes are already available for you to try in the 1.4.0-RC release!

We continue to highlight the upcoming changes in 1.4 release. In this blogpost, we want to describe a couple of important features related to coroutines:

  • New functionality to conveniently debug coroutines
  • The ability to define deep recursive functions

These changes are already available for you to try in the 1.4.0-RC release!

Kotlin 1.4.0-RC: Debugging coroutines

Let’s dive into details.

Debugging coroutines

Coroutines are great for asynchronous programming (but not only for that), and many people already use them or are starting to use them. When you write code with coroutines, however, trying to debug them can be a real pain. Coroutines jump between threads. It can be difficult to understand what a specific coroutine is doing or to check its context. And in some cases, tracking steps over breakpoints simply doesn’t work. As a result, you have to rely on logging or mental effort to debug the code with coroutines. To address this issue, we’re introducing new functionality in the Kotlin plugin that aims to make debugging coroutines much more convenient.

The Debug Tool Window now contains a new Coroutines tab. It is visible by default, and you can switch it on and off:

Kotlin 1.4.0-RC: Debugging coroutines

In this tab, you can find information about both currently running and suspended coroutines. The coroutines are grouped by the dispatcher they are running on. If you started a coroutine with a custom name, you can find it by this name in the Tool Window. In the following example, you can see that the main coroutine is running (we’ve stopped on a breakpoint inside it), and the other four coroutines are suspended:

Kotlin 1.4.0-RC: Debugging coroutines

import kotlinx.coroutines.*

fun main() = runBlocking {
   repeat(4) {
       launch(Dispatchers.Default + CoroutineName("Default-${'a' + it}")) {
           delay(100)
           val name = coroutineContext[CoroutineName.Key]?.name
           println("I'm '$name' coroutine")
       }
   }
   delay(50)
   // breakpoint
   println("I'm the main coroutine")
}

With the new functionality, you can check the state of each coroutine and see the values of local and captured variables. This also works for suspended coroutines!

In this example, we check the values of the local variables of suspended coroutines:

import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
   launch {
       val a = 3
       delay(300)
       println(a)
   }
   launch {
       val b = 2
       delay(200)
       println(b)
   }
   launch {
       val c = 1
       delay(100)
       // breakpoint here:
       println(c)
   }
}

Choose a suspended coroutine (click invokeSuspend to see its state on that point) and the Variables tab will show you the state of the local variables:

Kotlin 1.4.0-RC: Debugging coroutines

You can now see a full coroutine creation stack, as well as a call stack inside the coroutine:

Kotlin 1.4.0-RC: Debugging coroutines

Use the ‘Get Coroutines Dump’ option to get a full report containing the state of each coroutine and its stack:

Kotlin 1.4.0-RC: Debugging coroutines

At the moment, the coroutines dump is still rather simple, but we’re going to make it more readable and helpful in future versions.

Note that to make the debugger stop at a given breakpoint inside a coroutine, this breakpoint should have the “Suspend: All” option chosen for it:

Kotlin 1.4.0-RC: Debugging coroutines

To try this new functionality for debugging coroutines, you need to use the latest version of kotlinx.coroutines , 1.3.8-1.4.0-rc , and the latest version of the Kotlin plugin (e.g. 1.4.0-rc -release-IJ2020.1 -2 ).

The functionality is available only for Kotlin/JVM. If you encounter any problems (please don’t forget to share the details with us!), you can switch it off by opening Build, Execution, Deployment | Debugger | Data Views | Kotlin inPreferences and choosing Disable coroutines agent . For now, we’re releasing this functionality for debugging coroutines in the experimental state, and we’re looking forward to your feedback!

Defining deep recursive functions using coroutines

In Kotlin 1.4, you can define recursive functions and invoke them even when the call depth is greater than 100,000, using the standard library support based on coroutines!

Let’s first look at an ordinary recursive function, whose usage results in a StackOverflowError when the recursion depth gets too high. After that, we’ll discuss how you can fix the problem and rewrite the function using the Kotlin standard library.

We’ll use a simple binary tree, where each Tree node has a reference to its left and right children:

class Tree(val left: Tree?, val right: Tree?)

The depth of the tree is the length of the longest path from its root to its child nodes. It can be computed using the following recursive function:

fun depth(t: Tree?): Int =
   if (t == null) 0 else maxOf(
       depth(t.left),
       depth(t.right)
   ) + 1

The tree depth is the maximum of the depths of the left and right children increased by one. When the tree is empty it’s zero.

Kotlin 1.4.0-RC: Debugging coroutines

This function works fine when the recursion depth is small:

class Tree(val left: Tree?, val right: Tree?)

fun depth(t: Tree?): Int =
   if (t == null) 0 else maxOf(
       depth(t.left),
       depth(t.right)
   ) + 1

fun main() {
//sampleStart
    val tree = Tree(Tree(Tree(null, null), null), null)
    println(depth(tree)) // 3
//sampleEnd
}

However, if you create a tree with a depth greater than 100,000, which in practice is not so uncommon, you’ll get StackOverflowError as a result:

class Tree(val left: Tree?, val right: Tree?)

fun depth(t: Tree?): Int =
   if (t == null) 0 else maxOf(
       depth(t.left),
       depth(t.right)
   ) + 1
//sampleStart
fun main() {
   val n = 100_000
   val deepTree = generateSequence(Tree(null, null)) { prev ->
       Tree(prev, null)
   }.take(n).last()

   println(depth(deepTree))
}
/*
Exception in thread "main" java.lang.StackOverflowError
  at FileKt.depth(File.kt:5)
  ...
*/
//sampleEnd

The problem is that the call stack gets too large. To solve this issue, you can use a VM option to increase the maximum stack size. However, while this might work for specific use cases, it’s not a practical solution for the general case.

Alternatively, you can rewrite the code and store results for intermediate calls by hand in the heap rather than on the stack. This solution works in most cases and is common in other languages. However, the resulting code becomes non-trivial and complicated, and the beauty and simplicity of the initial function are lost. You can find an example here .

Kotlin now provides a clean way to solve this problem based on the coroutines machinery.

The Kotlin library now includes the definition DeepRecursiveFunction , which models recursive calls using the suspension mechanism:

class Tree(val left: Tree?, val right: Tree?)

@OptIn(ExperimentalStdlibApi::class)
val depthFunction = DeepRecursiveFunction<Tree?, Int> { t ->
   if (t == null) 0 else maxOf(
       callRecursive(t.left),
       callRecursive(t.right)
   ) + 1
}

@OptIn(ExperimentalStdlibApi::class)
fun depth(t: Tree) = depthFunction(t)

fun main() {
   val n = 100_000
   val deepTree = generateSequence(Tree(null, null)) { prev ->
       Tree(prev, null)
   }.take(n).last()

   println(depth(deepTree)) // 100000
}

You can compare the two versions, the initial one and the one using DeepRecursiveFunction , to make sure that the logic remains the same. Your new function now becomes a variable of type DeepRecursiveFunction , which you can call using the ‘invoke’ convention as depthFunction(t) . The function body now becomes the body of the lambda argument of DeepRecursiveFunction , and the recursive call is replaced with callRecursive . These changes are straightforward and easy to make. Note that while the new depth function uses coroutines under the hood, it is not itself a suspend function.

Understanding how DeepRecursiveFunction is implemented is interesting, but it is not necessary in order for you to use it and benefit from it. You can find the implementation details described in this blog post .

DeepRecursiveFunction is a part of the Kotlin standard library, not part of the kotlinx.coroutines library, since it’s not about asynchronous programming. At the moment this API is still experimental, so we’re looking forward to your feedback!

How to try it

As always, you can try Kotlin online at play.kotl.in .

In IntelliJ IDEA and Android Studio , you can update the Kotlin Plugin to version 1.4.0-RC. See how to do this .

If you want to work on existing projects that were created before installing the preview version, you need to configure your build for the preview version in Gradle or Maven . Note that unlike the previous preview versions, Kotlin 1.4.0-RC is also available directly from Maven Central. This means you won’t have to manually add the kotlin-eap repository to your build files.

You can download the command-line compiler from the Github release page .

Share your feedback

We’ll be very thankful if you find and report bugs to ourissue tracker. We’ll try to fix all the important issues before the final release, which means you won’t need to wait until the next Kotlin release for your issues to be addressed.

You are also welcome to join the #eap channel in Kotlin Slack (get an invite here ). In this channel, you can ask questions, participate in discussions, and get notifications about new preview builds.

Let’s Kotlin!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网络、群体与市场

网络、群体与市场

大卫·伊斯利(David Esley)、乔恩·克莱因伯格(Jon Kleinberg) / 李晓明、王卫红、杨韫利 / 清华大学出版社 / 2011-10-1 / CNY 69.00

过去十年来,现代社会中复杂的连通性向公众展现出与日俱增的魅力。这种连通性在许多方面都有体现并发挥着强大的作用,包括互联网的快速成长、全球通信的便捷,以及新闻与信息(及传染病与金融危机)以惊人的速度与强度传播的能力。这种现象涉及网络、动机和人们的聚合行为。网络将人们的行为联系起来,使得每个人的决定可能对他人产生微妙的后果。 本书是本科生的入门教材,同时也适合希望进入相关领域的高层次读者。它从交......一起来看看 《网络、群体与市场》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具