协程入门(一):启动与挂起

2,686 阅读3分钟

什么是协程

协程: 是一种更为灵活高效的"用户线程",能够选择异步还是同步执行,指定运行的线程。

异步、同步编程 : 是指的协程能够选择自身的启动模式,在当前线程堵塞式运行,还是在后台线程异步执行;

指定运行线程:能够方便的选择执行的线程是后台线程还是UI主线程;

在并发处理上会比使用线程来得更加的高效。

进程、线程、协程关系

进程 > 线程 > 协程, 即一个进程可以包含多个线程,一个线程上面可以运行多个协程。

启动方式

目前从非协程环境中启动协程环境主要有三种方式

启动方式 说明
runBlocking 创建新的协程,运行在当前线程上,所以会堵塞当前线程,直到协程体结束
GlobalScope.launch 启动一个新的线程,在新线程上创建运行协程,不堵塞当前线程
GlobalScope.asyn 启动一个新的线程,在新线程上创建运行协程,并且不堵塞当前线程,支持 通过await获取返回值

runBlocking

创建新的协程运行在当前线程上,所以会堵塞当前线程,直到协程体结束

适用范围

用于启动一个协程任务,通常只用于启动最外层的协程,例如线程环境切换到协程环境

示例

    /**
     * runBlocking创建新的协程运行在当前线程上,所以会堵塞当前线程,直到协程体结束
     *
     * 适用范围: 用于启动一个协程任务,通常只用于启动最外层的协程,例如线程环境切换到协程环境。
     *
     * 打印出:
     * current thread = main,1
     * runBlocking thread = main @coroutine#1,1
     * runBlocking end
     * current thread end
     */
    @Test
    fun runBlockingTest() {
        println("current thread = ${Thread.currentThread().name},${Thread.currentThread().id}")
        //runBlocking运行在当前线程上,堵塞当前线程
        runBlocking {
            println("runBlocking thread = ${Thread.currentThread().name},${Thread.currentThread().id}")
            delay(1000)
            println("runBlocking end")
        }
        //等待runBlocking协程体内的内容执行完毕,才继续执行
        println("current thread end")
    }

GlobalScope.launch

创建新的协程,默认运行在后台新的线程中,并且不堵塞当前线程

适用范围

适用范围: 需要启动异步线程处理的情况

示例
    /**
     * GlobalScope.launch默认运行在后台新的调度线程中,并且不堵塞当前线程
     *
     * 适用范围: 需要启动异步线程处理的情况
     *
     * 输出:
     * current thread = main, 1
     * current thread end
     * launch thread = DefaultDispatcher-worker-2 @coroutine#1, 12
     * launch thread end
     */
    @Test
    fun launchTest() {
        println("current thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
        //launch启动后台调度线程,并且不堵塞当前线程
        GlobalScope.launch {
            println("launch thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
            delay(1000)
            println("launch thread end")
        }
        println("current thread end")

        //当前线程休眠以便调度线程有机会执行
        Thread.sleep(3000)
    }

GlobalScope.async

创建新的协程,默认运行在后台新的线程中,并且不堵塞当前线程,支持通过await获取返回值

GlobalScope.async与GlobalScope.launch大致相同,

区别: 1.async返回类型为Deferred, launch返回类型为job 2.async可以在协程体中自定义返回值,并且通过Deferred.await堵塞当前线程等待接收async协程返回的类型。

适用范围

特别是需要启动异步线程处理并等待处理结果返回的场景

示例
    /**
     * async与launch的相同点:都是不堵塞当前线程并启动后台调度线程。
     * 区别: 1.async返回类型为Deferred, launch返回类型为job
     *       2.async可以在协程体中存在自定义的返回值,并且通过Deferred.await堵塞当前线程等待接收async协程返回的类型。
     *
     * 适用范围: 特别是需要启动异步线程处理并等待处理结果返回的场景
     *
     * 打印出:
     * current thread = main @coroutine#1, 1
     * current thread end
     * async thread = DefaultDispatcher-worker-1 @coroutine#2, 11
     * async end
     * result = 123
     */
    @Test
    fun asyncTest() {
        runBlocking {
            println("current thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
            //launch启动后台调度线程,并且不堵塞当前线程
            val deferred = GlobalScope.async {
                println("async thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
                delay(1000)
                println("async end")
                //需要通过标签的方式返回
                return@async "123"
            }
            println("current thread end")
            val result = deferred.await()
            println("result = $result")
            //当前线程休眠以便调度线程有机会执行
            Thread.sleep(3000)
        }
    }

启动方式

目前从非协程环境中启动协程环境主要有三种方式

启动方式 说明
runBlocking 创建新的协程,运行在当前线程上,所以会堵塞当前线程,直到协程体结束
GlobalScope.launch 启动一个新的线程,在新线程上创建运行协程,不堵塞当前线程
GlobalScope.asyn 启动一个新的线程,在新线程上创建运行协程,并且不堵塞当前线程,支持 通过await获取返回值

runBlocking

创建新的协程运行在当前线程上,所以会堵塞当前线程,直到协程体结束

适用范围

用于启动一个协程任务,通常只用于启动最外层的协程,例如线程环境切换到协程环境

示例

    /**
     * runBlocking创建新的协程运行在当前线程上,所以会堵塞当前线程,直到协程体结束
     *
     * 适用范围: 用于启动一个协程任务,通常只用于启动最外层的协程,例如线程环境切换到协程环境。
     *
     * 打印出:
     * current thread = main,1
     * runBlocking thread = main @coroutine#1,1
     * runBlocking end
     * current thread end
     */
    @Test
    fun runBlockingTest() {
        println("current thread = ${Thread.currentThread().name},${Thread.currentThread().id}")
        //runBlocking运行在当前线程上,堵塞当前线程
        runBlocking {
            println("runBlocking thread = ${Thread.currentThread().name},${Thread.currentThread().id}")
            delay(1000)
            println("runBlocking end")
        }
        //等待runBlocking协程体内的内容执行完毕,才继续执行
        println("current thread end")
    }

GlobalScope.launch

创建新的协程,默认运行在后台新的线程中,并且不堵塞当前线程

适用范围

适用范围: 需要启动异步线程处理的情况

示例
    /**
     * GlobalScope.launch默认运行在后台新的调度线程中,并且不堵塞当前线程
     *
     * 适用范围: 需要启动异步线程处理的情况
     *
     * 输出:
     * current thread = main, 1
     * current thread end
     * launch thread = DefaultDispatcher-worker-2 @coroutine#1, 12
     * launch thread end
     */
    @Test
    fun launchTest() {
        println("current thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
        //launch启动后台调度线程,并且不堵塞当前线程
        GlobalScope.launch {
            println("launch thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
            delay(1000)
            println("launch thread end")
        }
        println("current thread end")

        //当前线程休眠以便调度线程有机会执行
        Thread.sleep(3000)
    }

GlobalScope.async

创建新的协程,默认运行在后台新的线程中,并且不堵塞当前线程,支持通过await获取返回值

GlobalScope.async与GlobalScope.launch大致相同,

区别: 1.async返回类型为Deferred, launch返回类型为job 2.async可以在协程体中自定义返回值,并且通过Deferred.await堵塞当前线程等待接收async协程返回的类型。

适用范围

特别是需要启动异步线程处理并等待处理结果返回的场景

示例
    /**
     * async与launch的相同点:都是不堵塞当前线程并启动后台调度线程。
     * 区别: 1.async返回类型为Deferred, launch返回类型为job
     *       2.async可以在协程体中存在自定义的返回值,并且通过Deferred.await堵塞当前线程等待接收async协程返回的类型。
     *
     * 适用范围: 特别是需要启动异步线程处理并等待处理结果返回的场景
     *
     * 打印出:
     * current thread = main @coroutine#1, 1
     * current thread end
     * async thread = DefaultDispatcher-worker-1 @coroutine#2, 11
     * async end
     * result = 123
     */
    @Test
    fun asyncTest() {
        runBlocking {
            println("current thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
            //launch启动后台调度线程,并且不堵塞当前线程
            val deferred = GlobalScope.async {
                println("async thread = ${Thread.currentThread().name}, ${Thread.currentThread().id}")
                delay(1000)
                println("async end")
                //需要通过标签的方式返回
                return@async "123"
            }
            println("current thread end")
            val result = deferred.await()
            println("result = $result")
            //当前线程休眠以便调度线程有机会执行
            Thread.sleep(3000)
        }
    }

微信公众号