Kotlin-Coroutines

Kotlin-轻量级协程与线程执行比对分析

2021-06-13  本文已影响0人  蒋斌文

轻量级协程与线程执行比对分析

作用域构建器

除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一 个协程作用域并且在所有已启动子协程执行完毕之前不会结束
runBlocking 与 coroutineScope 可能看起来很类似,因为它们都会等待其协程体以及所有子协程结束。主要 区别在于,runBlocking 方法会阻塞当前线程来等待,而 coroutineScope 只是挂起,会释放底层线程用于其他 用途。由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数。

fun main() = runBlocking {
    launch {
        delay(1000)
        println("my job1")
    }

    println("person")

    coroutineScope {
        launch {
            delay(3000)
            println("my job2")
        }

        delay(2000)
        println("hello world")
    }//如果其中的协程挂起,那么coroutineScope函数也会挂起
    println("welcome")
}

RUN> 🏄🏄🏄🏄🏄🏄

person
my job1
hello world
my job2
welcome

Process finished with exit code 0
image-20210611164701768 image-20210611164857193

Creates a CoroutineScope and calls the specified suspend block with this scope. The provided scope inherits its coroutineContext from the outer scope, but overrides the context's Job.

创建一个coroutinscope并使用此范围调用指定的挂起块。提供的作用域从外部作用域继承其coroutineContext,但覆盖了上下文的Job。

轻量级协程与线程执行比对分析:

对于协程的效果跟线程其实差不多,但是它们俩其实还是存在本质的区别的,总的来说协程是轻量级的,而线程是重量级的。所以下面来做下实验来看一下它们两者的区别:

先来看下协程的效果,这里创建N个协程然后输出个字母,当然我们可以用for循环来创建多个,这里采用Kotlin的方式来实现一下:

fun main() = runBlocking {
    repeat(5000) {
        launch {
            delay(1000)
            println("A")
        }
    }

    println("Hello World")
}

repeat函数

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {//函数内部执行times循环
        action(index)
    }
}

Executes the given function action specified number of times.

执行给定action的函数times次。一个当前迭代基于0的索引会当成参数传给action

image-20210611165441396

比较容易,接下来咱们换成线程来实现同样的效果

fun main() {
    repeat(5000) {
        thread {
            Thread.sleep(1000)
            println("A")
        }
    }
    println("Hello World")
}

RUN> 🏄🏄🏄🏄🏄🏄

[0.780s][warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached.
Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached
  at java.base/java.lang.Thread.start0(Native Method)
  at java.base/java.lang.Thread.start(Thread.java:801)
  at kotlin.concurrent.ThreadsKt.thread(Thread.kt:42)
  at kotlin.concurrent.ThreadsKt.thread$default(Thread.kt:25)
  at com.shengsiyuan.coroutines.HelloKotlin9Kt.main(HelloKotlin9.kt:7)
  at com.shengsiyuan.coroutines.HelloKotlin9Kt.main(HelloKotlin9.kt)
[0.944s][warning][os,thread] Failed to start thread - pthread_create failed (EAGAIN) for attributes: stacksize: 1024k, guardsize: 4k, detached.
Error occurred during initialization of VM
java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached

Process finished with exit code 1

区别就出来啦!!!由于协程是用户态,没有个数的上限,而线程是内核态的,它需要依赖于系统,是有上限的,另外由于线程有切换的开销,所以在有些情况会能看到线程的执行速度要比协程要慢,当然在我电脑上木有看出来,验证出来的结论就是协程是轻量级的,而线程是重要级的

repeat()函数的一个细节,这个细节可能比较难理解,但是理解了之后能够让我们更加正确的用Kotlin的思维去编写代码,先来看一下它的参数定义:

image-20210611165947861 image-20210611170223625

我们看到的repeat()的第二个参数的Lambda表达式是要求要能接收一个参数呢,那怎么我们就能传lauch()函数给repeat()的第二个参数呢?

image-20210611170815166

这也是Kotlin跟Java不一样的地方,也就是如果只有一个参数时在写Lambda表达式是可以省略的,接下来我们来把这个隐含的参数打出来:

image-20210611171237191

这个细节非常之小,可能不一定每个人都能去研究它,但是研究透了之后其实也就是让我们更好的能使用Kotlin的一个基石。

上一篇下一篇

猜你喜欢

热点阅读