Kotlin Coroutines 笔记 (二)

Kotlin Coroutines 笔记 (二)_第1张图片


协程虽然是微线程,但是并不会和某一个特定的线程绑定,它可以在A线程中执行,并经过某一个时刻的挂起(suspend),等下次调度到恢复执行的时候,很可能会在B线程中执行。

一. withContext

与 launch、async、runBlocking 类似 withContext 也属于 Coroutine builders。不过与他们不同的是,其他几个都是创建一个新的协程,而 withContext 不会创建新的协程。withContext 允许更改协程的执行线程,withContext 在使用时需要传递一个 CoroutineContext 。

 
   
  1.    launch {

  2.        val result1 = withContext(CommonPool) {

  3.            delay(2000)

  4.            1

  5.        }

  6.        val  result2 = withContext(CommonPool) {

  7.            delay(1000)

  8.            2

  9.        }

  10.        val  result = result1 + result2

  11.        println(result)

  12.    }

  13.    Thread.sleep(5000)

执行结果:

 
   
  1. 3

withContext 可以有返回值,这一点类似 async。async 创建的协程通过 await() 方法将值返回。而 withContext 可以直接返回。

 
   
  1.    launch {

  2.        val result1 = async {

  3.            delay(2000)

  4.            1

  5.        }

  6.        val  result2 = async {

  7.            delay(1000)

  8.            2

  9.        }

  10.        val  result = result1.await() + result2.await()

  11.        println(result)

  12.    }

  13.    Thread.sleep(5000)

执行结果:

 
   
  1. 3

二. 共享线程池

在上述的例子中,withContext 使用了 CommonPool。CommonPool 继承了 CoroutineDispatcher,表示使用线程池来执行协程的任务。

Kotlin Coroutines 笔记 (二)_第2张图片


CommonPool 有点类似于 RxJava 的 Schedulers.computation(),主要是用于CPU密集型的计算任务。

CommonPool 使用 pool 来执行 block。

 
   
  1.    override fun dispatch(context: CoroutineContext, block: Runnable) =

  2.        try { (pool ?: getOrCreatePoolSync()).execute(timeSource.trackTask(block)) }

  3.        catch (e: RejectedExecutionException) {

  4.            timeSource.unTrackTask()

  5.            DefaultExecutor.execute(block)

  6.        }

如果 pool 为空,则调用 getOrCreatePoolSync() 方法来创建 pool。

 
   
  1.    @Synchronized

  2.    private fun getOrCreatePoolSync(): Executor =

  3.        pool ?: createPool().also { pool = it }

此时,createPool() 方法是正在创建 pool 的方法。

首先,安全管理器不为空的话,使用 createPlainPool() 来创建 pool。 否则,尝试创建一个 ForkJoinPool,不行的话还是使用 createPlainPool() 来创建 pool。

 
   
  1.    private fun createPool(): ExecutorService {

  2.        if (System.getSecurityManager() != null) return createPlainPool()

  3.        val fjpClass = Try { Class.forName("java.util.concurrent.ForkJoinPool") }

  4.            ?: return createPlainPool()

  5.        if (!usePrivatePool) {

  6.            Try { fjpClass.getMethod("commonPool")?.invoke(null) as? ExecutorService }

  7.                ?.let { return it }

  8.        }

  9.        Try { fjpClass.getConstructor(Int::class.java).newInstance(parallelism) as? ExecutorService }

  10.            ?. let { return it }

  11.        return createPlainPool()

  12.    }

createPlainPool() 会使用 Executors.newFixedThreadPool() 来创建线程池。

 
   
  1.    private fun createPlainPool(): ExecutorService {

  2.        val threadId = AtomicInteger()

  3.        return Executors.newFixedThreadPool(parallelism) {

  4.            Thread(it, "CommonPool-worker-${threadId.incrementAndGet()}").apply { isDaemon = true }

  5.        }

  6.    }

CommonPool 的创建原理大致了解之后,通过源码发现 CoroutineContext 默认的 CoroutineDispatcher 就是 CommonPool。

 
   
  1. /**

  2. * This is the default [CoroutineDispatcher] that is used by all standard builders like

  3. * [launch], [async], etc if no dispatcher nor any other [ContinuationInterceptor] is specified in their context.

  4. *

  5. * It is currently equal to [CommonPool], but the value is subject to change in the future.

  6. */

  7. @Suppress("PropertyName")

  8. public actual val DefaultDispatcher: CoroutineDispatcher = CommonPool

常见的 CoroutineDispatcher 还可以通过 ThreadPoolDispatcher 的 newSingleThreadContext()、newFixedThreadPoolContext()来创建,以及Executor 的扩展函数 asCoroutineDispatcher() 来创建。

在 Android 中,还可以使用UI。它顾名思义,在 Android 主线程上调度执行。

三. 可取消的协程

Job、Deferred 对象都可以取消任务。

3.1 cancel()

使用 cancel() 方法:

 
   
  1.    val job = launch {

  2.        delay(1000)

  3.        println("Hello World!")

  4.    }

  5.    job.cancel()

  6.    println(job.isCancelled)

  7.    Thread.sleep(2000)

执行结果:

 
   
  1. true

true表示job已经被取消了,并没有打印"Hello World!"

3.2 cancelAndJoin()

使用 cancelAndJoin() 方法:

 
   
  1.    runBlocking<Unit> {

  2.        val job = launch {

  3.            repeat(100) { i ->

  4.                println("count time: $i")

  5.                delay(500)

  6.            }

  7.        }

  8.        delay(2100)

  9.        job.cancelAndJoin()

  10.    }

执行结果:

 
   
  1. count time: 0

  2. count time: 1

  3. count time: 2

  4. count time: 3

  5. count time: 4

cancelAndJoin() 等价于使用了 cancel() 和 join()。

join() 方法用于等待已启动协程的完成,并且它不会传播其异常。 但是,崩溃的子协程也会取消其父协程,并带有相应的异常。

3.3 检查协程的取消标记

如果一个协程一直在执行计算,没有去检查取消标记,它就无法取消。即使调用了cancel() 或者 cancelAndJoin()。

 
   
  1.    runBlocking<Unit> {

  2.        val startTime = System.currentTimeMillis()

  3.        val job = launch {

  4.            var tempTime = startTime

  5.            var i = 0

  6.            while (i < 100) {

  7.                if (System.currentTimeMillis() >= tempTime) {

  8.                    println("count time: ${i++}")

  9.                    tempTime += 500L

  10.                }

  11.            }

  12.        }

  13.        delay(2100)

  14.        job.cancelAndJoin()

  15.    }

上述代码仍然会打印100次。

如果使用 isActive检查取消标记,则Job 或 Deferred 的任务可以被取消:

 
   
  1.    runBlocking<Unit> {

  2.        val startTime = System.currentTimeMillis()

  3.        val job = launch {

  4.            var tempTime = startTime

  5.            var i = 0

  6.            while (isActive) {

  7.                if (System.currentTimeMillis() >= tempTime) {

  8.                    println("count time: ${i++}")

  9.                    tempTime += 500L

  10.                }

  11.            }

  12.        }

  13.        delay(2100)

  14.        job.cancelAndJoin()

  15.    }

执行结果:

 
   
  1. count time: 0

  2. count time: 1

  3. count time: 2

  4. count time: 3

  5. count time: 4

isActive 是 CoroutineScope 的属性:

 
   
  1. package kotlinx.coroutines.experimental

  2. import kotlin.coroutines.experimental.*

  3. import kotlin.internal.*

  4. /**

  5. * Receiver interface for generic coroutine builders, so that the code inside coroutine has a convenient

  6. * and fast access to its own cancellation status via [isActive].

  7. */

  8. public interface CoroutineScope {

  9.    /**

  10.     * Returns `true` when this coroutine is still active (has not completed and was not cancelled yet).

  11.     *

  12.     * Check this property in long-running computation loops to support cancellation:

  13.     * ```

  14.     * while (isActive) {

  15.     *     // do some computation

  16.     * }

  17.     * ```

  18.     *

  19.     * This property is a shortcut for `coroutineContext.isActive` in the scope when

  20.     * [CoroutineScope] is available.

  21.     * See [coroutineContext][kotlin.coroutines.experimental.coroutineContext],

  22.     * [isActive][kotlinx.coroutines.experimental.isActive] and [Job.isActive].

  23.     */

  24.    public val isActive: Boolean

  25.    /**

  26.     * Returns the context of this coroutine.

  27.     *

  28.     * @suppress: **Deprecated**: Replaced with top-level [kotlin.coroutines.experimental.coroutineContext].

  29.     */

  30.    @Deprecated("Replace with top-level coroutineContext",

  31.        replaceWith = ReplaceWith("coroutineContext",

  32.            imports = ["kotlin.coroutines.experimental.coroutineContext"]))

  33.    @LowPriorityInOverloadResolution

  34.    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")

  35.    public val coroutineContext: CoroutineContext

  36. }

总结:

本文介绍了三个部分:withContext的使用,CommonPool的创建以及如何取消协程。其中,还捎带介绍了 async 和 await 的使用。

该系列的相关文章:

Kotlin Coroutines 笔记 (一)



关注【Java与Android技术栈】

新增了关键词回复,赶紧来调戏本公众号吧~


更多精彩内容请关注扫码

Kotlin Coroutines 笔记 (二)_第3张图片


你可能感兴趣的:(Kotlin Coroutines 笔记 (二))