Coroutine 基础

我们将介绍协程的基本概念。

第一个协程程序

我们把下面的代码跑起来:

import kotlinx.coroutines.*fun main() {GlobalScope.launch { // launch new coroutine in background and continuedelay(1000L) // non-blocking delay for 1 second (default time unit is ms)println("World!") // print after delay}println("Hello,") // main thread continues while coroutine is delayedThread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

我们可以看到输出:

Hello,
World!

本质上,协程是轻量级的线程。可以用协程构造器 launch 来启动协程,使其运行在 CoroutineScope 环境上下文中。我们在 GlobalScope 启动一个新的协程,它的生命周期仅受整个应用生命周期的限制。

我们可以尝试把 GlobalScope.launch{...} 替换为 thread{...} ,把 delay(...) 替换为 Thread.sleep(...) 。我们可以得到相同的结果。

假如我们仅仅把 GlobalScope.launch 替换为 thread ,编译器将会报错:

Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another suspend function

这是因为 delay 是特殊的挂起函数,它不会阻塞线程,但是这种特殊的函数只能在协程环境中调用。

桥接阻塞和非阻塞

上面那个例子混合非阻塞函数 delay(...) 和阻塞函数 Thread.sleep(...) 在同一段代码中,我们不能比较清晰的看出哪个是阻塞的,哪个是非阻塞的。我们让它运行在 runBlocking 协程中:

import kotlinx.coroutines.*fun main() { GlobalScope.launch { // launch new coroutine in background and continuedelay(1000L)println("World!")}println("Hello,") // main thread continues here immediatelyrunBlocking {     // but this expression blocks the main threaddelay(2000L)  // ... while we delay for 2 seconds to keep JVM alive}
}

这个结果是相同的,但是整段代码只使用了非阻塞函数 delay(),主线程调用 runBlocking,将会阻塞,知道 runBlocking 协程内部执完成。

我们可以换种更常见的方式来编写:

import kotlinx.coroutines.*fun main() = runBlocking<Unit> { // start main coroutineGlobalScope.launch { // launch new coroutine in background and continuedelay(1000L)println("World!")}println("Hello,") // main coroutine continues here immediatelydelay(2000L)      // delaying for 2 seconds to keep JVM alive
}

上面的例子中,我们明确指定了返回值,Unit

等待job完成

使用延时函数来等待协程的完成不是一个好的实现。让我们使用一种非阻塞的方法明确的等待协程的启动和执行完成。

import kotlinx.coroutines.*fun main() = runBlocking {
//sampleStartval job = GlobalScope.launch { // launch new coroutine and keep a reference to its Jobdelay(1000L)println("World!")}println("Hello,")job.join() // wait until child coroutine completes
//sampleEnd
}

现在,这个执行结果是一样的,但我们的代码更加的合理。

结构化并发

在实际应用中我们还有其他要考虑的。我们使用 GlobalScope.launch 创建了协程,虽然它是轻量级的,但是它还是会消耗内存资源。如果我们忘记引用了,我们新启动的协程还是会在运行。如果我们的协程里执行了需要比较长时间的操作,(比如等待很长时间),或者我们启动了很多的协程,这将会造成内存泄漏。手动的保持每个协程的引用是易于出错的处理的方式。

我们有更合理的解决方法。我们不使用全局的 GlobalScope 协程启动构造器,我们仅在特定的上下文函数中启动。

在我们的例子中,我们使用了 runBlocking 协程构造器,每一个协程构造器,都将会生成一个上下文环境实例 CoroutineScope。我们可以在该上下文环境实例中启动一个新的协程,然后就不能明确的使用 join 等方法了。因为外层的协程函数不会先结束,除非在它上下文环境中启动的所有的协程都已经执行完成。因此,我们的例子可以更加简洁:

import kotlinx.coroutines.*fun main() = runBlocking { // this: CoroutineScopelaunch { // launch new coroutine in the scope of runBlockingdelay(1000L)println("World!")}println("Hello,")
}
上下文构造器

此外,有不同的构造器提供协程上下文环境,我们可以使用 coroutineScope 构造器来声明自己的上下文环境。它将会新构建一个等待所有内部协程完成的上下文环境。和 runBlocking 主要的不同是,它在等待的时候并不阻塞当前线程。

import kotlinx.coroutines.*fun main() = runBlocking { // this: CoroutineScopelaunch { delay(200L)println("Task from runBlocking")}coroutineScope { // Creates a new coroutine scopelaunch {delay(500L) println("Task from nested launch")}delay(100L)println("Task from coroutine scope") // This line will be printed before nested launch}println("Coroutine scope is over") // This line is not printed until nested launch completes
}
提取函数重构

我们把 launch{...} 里边的代码块提取出来,如果使用IDE重构函数方式的化,我们可以看到 suspend 修饰符已经被添加在函数前边。挂起函数可以像常规函数一样在协程上下文中使用。但它具有的特有的特性是,它能够在协程上下文中挂起,然后在某个时刻恢复到原来的执行点继续执行。

import kotlinx.coroutines.*fun main() = runBlocking {launch { doWorld() }println("Hello,")
}// this is your first suspending function
suspend fun doWorld() {delay(1000L)println("World!")
}

不建议在提取的方法中再调用协程构造器启动另一个协程上下文环境,因为这样使得结构不清晰。

协程是轻量化的
import kotlinx.coroutines.*fun main() = runBlocking {repeat(100_000) { // launch a lot of coroutineslaunch {delay(1000L)print(".")}}
}

我们启动了10万个协程,每个协程中延时一秒后打印一个 . ,如果我们换成线程的话,很可能抛出内存溢出错误。?

#####全局上下文的协程想守护线程

我们使用 GlobalScope 每一秒打印一次 I'm sleeping , 然后在主线程中延时,然后返回。

import kotlinx.coroutines.*fun main() = runBlocking {
//sampleStartGlobalScope.launch {repeat(1000) { i ->println("I'm sleeping $i ...")delay(500L)}}delay(1300L) // just quit after delay
//sampleEnd
}

我们可以看到打印了三行:

I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...

GlobalScope 启动的协程并不保存进程的存活,它们和守护线程相似。

Coroutine 基础相关推荐

  1. Lua基础之coroutine(协程)

    为什么80%的码农都做不了架构师?>>>    概括:1.创建协程2.coroutine的函数3.coroutine的基本流程4.yield对coroutine流程的干预5.resu ...

  2. Python基础入门教程:使用 Python 3 协程快速获得一个代理池

    Python基础入门教程:使用 Python 3 协程快速获得一个代理池 前言 在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞.比如我们使用 requests 库来进行网络爬虫请求的话, ...

  3. 深入理解 Kotlin coroutine (二)

    原文链接:https://github.com/enbandari/Kotlin-Tutorials 上周我们把 Kotlin Coroutine 的基本 API 挨个讲了一下,也给出了一些简单的封装 ...

  4. 深入理解 Kotlin Coroutine (一)

    原文链接:https://github.com/enbandari/Kotlin-Tutorials 本文主要介绍 Kotlin Coroutine 的基础 API,有关 Kotlinx.Corout ...

  5. Coroutine in Java - Quasar Fiber实现--转载

    转自 https://segmentfault.com/a/1190000006079389?from=groupmessage&isappinstalled=0 简介 说到协程(Corout ...

  6. Go游戏服务器开发的一些思考(十):goroutine和coroutine

    概要 go语言的特色之一就是goroutine.也就是go协程.由于协程这个东西在go语言之前,用到相对比较少,大家对协程的理解程度不一,或有偏差.比如本人刚接触goroutine时,就对其比较畏惧, ...

  7. Unity 协程Coroutine综合测试

    Unity 协程Coroutine综合测试 1 using UnityEngine; 2 using System.Collections; 3 using System.Text; 4 5 publ ...

  8. Unity协程(Coroutine)原理深入剖析再续

    Unity协程(Coroutine)原理深入剖析再续 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 前面已经介绍过对协程(Coroutine ...

  9. Lua === Lua 十分钟基础入门上手

    Lua维基百科 Lua的目标是成为一个很容易嵌入到其他语言中使用的语言: 很多程序员使用Lua作为自己的嵌入式脚本语言,实现可配置性,可扩展性: 轻量级语言,体积小速度启动快,只包含一个精简的核心和基 ...

最新文章

  1. COLING 2018 ⽤对抗增强的端到端模型⽣成合理且多样的故事结尾
  2. 【CentOS 7MySQL常用操作3】,MySQL常用命令#180113
  3. Python运算符与Pandas方法的映射关系
  4. AIX 中关于文件存储的相关命令
  5. Java之反射代码演示说明
  6. 图片和图形之性能和视图层次结构(18)
  7. Excel工作表密码保护的破解
  8. 牛客 - 膜法记录(状压dp预处理)
  9. #6073. 「2017 山东一轮集训 Day5」距离(树链剖分 + 永久标记主席树)
  10. 《dp补卡——多重背包》
  11. EF实体框架数据操作基类(转)
  12. 企业数字化转型到底是什么?带你一图看懂答案
  13. PCA主成分分析实战和可视化 | 附R代码和测试数据
  14. WCF(一) ---- 简单调用
  15. 嵩天python123测试3_嵩天老师python123测验3: 基本数据类型 (第3周)
  16. Javaweb(html+css+javascript)
  17. Japan games Android,Learn Japanese with Games
  18. html字体库的是引入----阿里妈妈
  19. vue echarts 实现速度的那种进度蓄力效果
  20. Yii Framework 开发教程(32) Zii组件-GridView示例

热门文章

  1. “机器学习”三重门_“中庸之道”趋若人(深度学习入门系列之四)
  2. 计算机组成部分有cpu吗,CPU的主要组成部分有哪些?
  3. 图像隐写分析-Markov特征编程实现
  4. java中文半角转全角_java对于半角和全角的转换
  5. 个税计算器支持相关请回复本帖
  6. Anatomy of an Android Application
  7. Linux内存管理之slab 1:slab原理(+buddy伙伴系统)
  8. 字节跳动 2024 校园招聘研发提前批正式启动!
  9. rman备份恢复详细方案和介绍
  10. 离职的原因 --- 写给那些想要跳槽的人们