重拾 Kotlin 协程——调度器(1)

451 阅读4分钟

基础使用

我们若要使用到 Kotlin 协程,首先需要添加依赖:

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
}

然后通过以下代码就可以启动协程:

        CoroutineScope(Dispatchers.Main).launch {

        }

这样,就能够在主线程中执行代码了。

调度器

而这个调度器不仅只有 Dispatchers.Main 这种,还有:

  • Dispatchers.Main
  • Dispatchers.Unconfined
  • Dispatchers.Default
  • Dispatchers.IO

温馨提示:每个调度器里面所写的结论只针对于当前的调度器,并不是针对协程整体来讲


Dispatchers.Main

测试代码:

    val startTime = System.currentTimeMillis()
    println("--1--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    CoroutineScope(Dispatchers.Main).launch {
        println("--2--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        Thread.sleep(3000)
        println("--3--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    }
    println("--4--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    Thread.sleep(3000)
    println("--5--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")

输出结果:

I/System.out: --1--main--time:0
I/System.out: --4--main--time:62
I/System.out: --5--main--time:3064
I/System.out: --2--main--time:3095
I/System.out: --3--main--time:6098

阶段性结论:

  • 协程里面的代码在主线程执行
  • 协程外的代码和协程内的代码即使都是在主线程中执行,但是,并不会直接顺序执行,而且继续执行下,等某个时刻再执行协程里面的代码
  • 若协程外的主线程阻塞了,会等主线程阻塞完才会执行协程里面的代码

不过,在这里有个疑问,就是第二点中的某个时刻该怎么理解?

  • 它是一定等协程外的代码执行完才会执行协程里面的代码吗?
  • 还是协程外的代码还没完,就会切到协程里面进行执行?

在这里,我模拟下在协程外执行一些耗时的操作:

测试代码:

    val startTime = System.currentTimeMillis()
    println("--1--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    CoroutineScope(Dispatchers.Main).launch {
        println("--2--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        Thread.sleep(3000)
        println("--3--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    }
    println("--4--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
//        Thread.sleep(3000)
    for(index in 0..10000000000){}
    println("--5--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")

输出结果:

I/System.out: --1--main--time:0
I/System.out: --4--main--time:67
I/System.out: --5--main--time:12738
I/System.out: --2--main--time:12765
I/System.out: --3--main--time:15766

可以初步理解为:只有协程外部的代码执行完才会执行协程内部的代码

Dispatchers.Main 总结

  • 协程里面的代码在主线程执行
  • 协程外的代码和协程内的代码即使都是在主线程中执行,但是,并不会直接顺序执行,而且继续执行下去
  • 若协程外的主线程阻塞了,会等主线程阻塞完才会执行协程里面的代码
  • 只有协程外部的代码执行完才会执行协程内部的代码

Dispatchers.Unconfined

测试代码:

    val startTime = System.currentTimeMillis()
    println("--1--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    CoroutineScope(Dispatchers.Unconfined).launch {
        println("--2--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        Thread.sleep(3000)
        println("--3--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    }
    println("--4--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    for(index in 0..10000000000){}
    println("--5--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")

输出结果:

I/System.out: --1--main--time:0
I/System.out: --2--main--time:52
I/System.out: --3--main--time:3053
I/System.out: --4--main--time:3054
I/System.out: --5--main--time:15267

阶段性结论:

  • 在主线程中执行
  • 能跟协程外部的代码顺序执行下去

不过这里有一个很明显的问题,既然是顺序执行下去,那么,Dispatchers.Unconfined 还有什么意义?

另外,若外部线程不是主线程,那么,Dispatchers.Unconfined 还会是在主线程执行吗?还会顺序执行吗?

在这里,我尝试在子线程中执行 Dispatchers.Unconfined ,并且在 Dispatchers.Unconfined 中执行挂起操作:

测试代码:

    Thread {

        val startTime = System.currentTimeMillis()

        println("--1--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        CoroutineScope(Dispatchers.Unconfined).launch {
            println("--2--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
//            Thread.sleep(3000)
            withContext(Dispatchers.IO) {

            }
            println("--3--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        }
        println("--4--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        for (index in 0..10000000000) {
        }
        println("--5--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    }.start()

输出结果:

I/System.out: --1--Thread-2--time:0
I/System.out: --2--Thread-2--time:155
I/System.out: --4--Thread-2--time:284
I/System.out: --3--DefaultDispatcher-worker-1--time:286
I/System.out: --5--Thread-2--time:18155

阶段性结论:

  • Dispatchers.Unconfined 并不是一定会在主线程执行,而是会延续协程外的线程,并通过在遇到挂起操作前顺序执行。
  • Dispatchers.Unconfined 的代码并不是一直在同个线程中执行,在遇到挂起操作之后,会开启新的线程去执行代码。

Dispatchers.Unconfined 总结

  • 协程里面的代码在遇到挂起操作之前,会延续协程外的线程进行顺序执行
  • 在遇到挂起操作之后,会开启新的线程去执行代码。

Dispatchers.Default、Dispatchers.IO

测试代码:

     Thread {

        val startTime = System.currentTimeMillis()

        println("--1--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        CoroutineScope(Dispatchers.Default).launch {
            println("--2--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
//            Thread.sleep(3000)
            withContext(Dispatchers.IO) {

            }
            println("--3--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        }
        println("--4--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
        for (index in 0..10000000000) {
        }
        println("--5--${Thread.currentThread().name}--time:${System.currentTimeMillis() - startTime}")
    }.start()

输出结果:

I/System.out: --1--Thread-2--time:0
I/System.out: --4--Thread-2--time:117
I/System.out: --2--DefaultDispatcher-worker-1--time:120
I/System.out: --3--DefaultDispatcher-worker-1--time:174
I/System.out: --5--Thread-2--time:16104

Dispatchers.Default、Dispatchers.IO 总结

  • 协程里面的代码会重新开启一个线程进行执行,非主线程