Kotlin协程:启动协程

Kotlin 启动协程有 3 种方式:launch、async 和 runBlocking。

在使用协程之前需要引入协程库依赖。

"org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines"

在运行示例前,配置协程调试 VM 参数:
Edit config -> VM options

-Dkotlinx.coroutines.debug

launch

launch用来启动协程,但是不需要获取运行后的返回结果。它类似于”射箭“场景,将箭发射出去,但是不需要箭返回。使用者不关心协程的返回值。

launch 使用方法

使用 launch 时需要一个协程作用域,这里先使用 GlobalScope。

fun main() {
    GlobalScope.launch {
        println("Coroutine started: ${Thread.currentThread().name}")
        delay(1000L)
        println("Hello World!")
    }
    
    println("After launch:${Thread.currentThread().name}")
    Thread.sleep(2000L)
}

输出结果

After launch:main
Coroutine started: DefaultDispatcher-worker-1 @coroutine#1
Hello World!

Process finished with exit code 0

可以看出先执行 main 的代码,再执行 launch 中的代码。launch 中使用了 delay 方法延迟 1 秒,然后打印语句。主线程休眠 2 秒后程序运行结束。

因为配置了 -Dkotlinx.coroutines.debug 参数,launch 打印线程名称时也打印了协程名称。

Coroutine started: DefaultDispatcher-worker-1 @coroutine#1

协程 coroutine#1 运行在 DefaultDispatcher-worker-1 线程。

如果去掉最后一句 sleep,程序没有输出结果。

fun main() {
    GlobalScope.launch {
        println("Coroutine started: ${Thread.currentThread().name}")
        delay(1000L)
        println("Hello World!")
    }

    println("After launch:${Thread.currentThread().name}")
//    Thread.sleep(2000L)
}

输出结果

After launch:main

Process finished with exit code 0

可以看出 launch 启动的协程没有运行。这是因为此时 launch 的方法执行在守护线程。守护线程的特点是如果所有非守护线程运行结束,守护线程会自动停止。在 main 线程运行结束后,launch 停止,里面的代码没有执行。

如果我们将 launch 中的 delay 替换成 sleep,编译器会提示:Inappropriate blocking method call

fun main() {
    GlobalScope.launch {
        println("Coroutine started: ${Thread.currentThread().name}")
        // Inappropriate blocking method call 
        Thread.sleep(1000L)
        println("Hello World!")
    }

    println("After launch:${Thread.currentThread().name}")
    Thread.sleep(2000L)
}

因为协程中应该运行非阻塞的函数,而 delay 就是一个非阻塞的挂起函数。挂起函数的特点就是”挂起-恢复“,是非阻塞的。

delay 函数定义,具有 suspend 关键词。

public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
    }
}

launch 介绍

launch 方法的源码如下:

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
    LazyStandaloneCoroutine(newContext, block) else
    StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

launch 是 CoroutineScope 的扩展方法,只能在 CoroutineScope 的内部运行。因此在使用时需要一个协程作用域。
GlobalScope.launch 表示协程运行在全局作用域 GlobalScope。

launch 方法有 3 个参数:context,start,block。分别是 CoroutineContext、CoroutineStart 和 suspend CoroutineScope.() -> Unit 类型。

CoroutineContext 是协程上下文,通常用来指定协程的执行线程。

CoroutineStart 是协程启动方式,默认是 DEFAULT 方式。DEFAULT 表示协程立即执行。还有一个 LAZY 方式,表示协程懒加载执行。

suspend CoroutineScope.() -> Unit 表示协程需要执行的匿名函数类型。这个函数是挂起函数,用 suspend 关键字表示。它是 CoroutineScope 的扩展函数,用 CoroutineScope.() -> Unit 表示。() -> Unit 表示函数入参为空,返回值也为空。

runBlocking

runBlocking 也能用来启动协程,但是它是阻塞的。

runBlocking 使用


fun main() {
    runBlocking {                       // 1
        println("Coroutine started!")   // 2
        delay(1000L)                    // 3
        println("Hello World!")         // 4
    }

    println("After launch!")            // 5
    Thread.sleep(2000L)                 // 6
    println("Process end!")             // 7
}

输出结果:

Coroutine started!
Hello World!
After launch!
Process end!

可以看出 runBlocking 是顺序执行的。main 里面的代码会等待 runBlocking 中的代码执行完毕。

runBlocking 介绍

runBlocking 是一个顶层函数,定义在 Builders.kt。
runBlocking 在内部构造了 newContext 协程上下文,然后调用 start 方法启动。最后使用 joinBlocking 返回。

@Throws(InterruptedException::class)
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    val currentThread = Thread.currentThread()
    val contextInterceptor = context[ContinuationInterceptor]
    val eventLoop: EventLoop?
    val newContext: CoroutineContext
    if (contextInterceptor == null) {
        // create or use private event loop if no dispatcher is specified
        eventLoop = ThreadLocalEventLoop.eventLoop
        newContext = GlobalScope.newCoroutineContext(context + eventLoop)
    } else {
        // See if context's interceptor is an event loop that we shall use (to support TestContext)
        // or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
        eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }
            ?: ThreadLocalEventLoop.currentOrNull()
        newContext = GlobalScope.newCoroutineContext(context)
    }
    val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
    coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
    return coroutine.joinBlocking()
}

runBlocking 的最后一个参数是block: suspend CoroutineScope.() -> T),同时也返回 T。因此它可以返回协程执行结果。

runBlocking 是对 launch 的一种补充,但由于它是阻塞式的,因此,runBlocking 并不适用于实际的工作当中。

async

async 使用


fun main() = runBlocking {
    println("In runBlocking:${Thread.currentThread().name}")

    val deferred: Deferred<String> = async {
        println("In async:${Thread.currentThread().name}")
        delay(1000L) // 模拟耗时操作
        return@async "Task completed!"
    }

    println("After async:${Thread.currentThread().name}")

    val result = deferred.await()
    println("Result is: $result")
}

输出结果

In runBlocking:main @coroutine#1
After async:main @coroutine#1 // 注意,它比“In async”先输出
In async:main @coroutine#2
Result is: Task completed!

可以看出 async 是非阻塞的,同时用 await 返回执行结果。

async 介绍

async 的行为类似钓鱼模式,抛出鱼竿,然后拉回鱼竿。

public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyDeferredCoroutine(newContext, block) else
        DeferredCoroutine<T>(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

async 返回 Deferred 类型,用来获取结果。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值