协程(Coroutines)

  • 阻塞和挂起
  • 挂起函数
  • @RestrictsSuspension注解
  • 协程内部机制
  • 协程实验状态
  • 标准API
    • 底层API:kotlin。coroutines
    • kotlin.coroutines中的生成器API
    • 其他高级API:kotlinx.coroutines

协程

协程:有些API需要长时间运行,并且需要调用者阻塞其他程序知道这些调用完成(比如网络IO、文件IO、CPU或者GPU比较集中的工作)协程提供了一种避免线程阻塞并用一种更轻量级,更易操控到操作:协程暂停

协程把异步编程放入库中来简化这类操作。程序逻辑在携程中顺序表述,而底层的库会将其转为异步操作。库会将相关的用户代码打包成回调,订阅相关事件,调度其执行到不同的线程(甚至不同的机器),而代码依然像顺序执行那么简单

1.阻塞和挂起

协程和线程的不同

  • 协程是一种可以不阻塞线程但却可以被挂起的计算过程:尤其在高负载情形下,因为只有小部分线程是实际运行的,因此阻塞他们会导致一些重要任务呗延迟
  • 协程挂起没有什么开销,没有上下文切换或者任何操作系统的介入。最重要的是,挂起是可以被用户库控制的,库的作者可以决定在挂起时根据需要进行一些优化/日志/拦截等操作
  • 协程不能被任意的操作挂起,而仅仅可以在被标记为挂起点的地方挂起

挂起函数 ???调用不成功,不知道使用出了什么问题导致

这样的函数被称为挂起函数,因为调用它可能导致挂起协程(库可以在调用结果已经存在的情形下决定取消挂起)。挂起函数可以想正常函数那样接受参数返回结果,但只能在协程中调用或者被其他挂起函数调用。事实上启动一个协程至少需要一个挂起函数,而且常常是匿名(比如lambda)。下面这个例子是一个简单的async()函数

fun <T> async(block:suspend()->T)

这里的async()只是一个普通的函数(不是挂起函数),但block参数是一个带suspend修饰的函数类型,所以当传递一个lambda给async()时,这回事一个挂起lambda,这样我们就可以在这里调用一个挂起函数

async{
    doSomething(foo)
}

继续类比。await()函数可以是一个挂起函数(因此在await(){})语句块内仍然可以调用,该函数会挂起协程直至指定操作完成并返回结果:

async{
    val result=computation.await()
}

注意await()doSomething()不能再像main()这样的普通函数中调用:挂起函数只能被协程调用

还有一点,挂起函数可以是虚函数,当复写他们,必须制定suspend修饰符
interface Base{
    suspend fun foo()
}

class Derived:Base{
    override suspend fun foo(){}
}

@RestrictsSuspension注解

扩展函数(以及lambda)可以被标记为suspend。这样方便用户创建其他**DSLs(通过lambda函数接口定义出来的面向特定问题的语言)**以及扩展其他API。不过有些情况,库的作者需要阻止用户添加新的挂起线程的方案

就需要@Restrictsuspension注解了,当一个接受类或者接口R被标注时,所有课挂起扩展都需要代理R的成员或者其他扩展。由于扩展不能互相无限代理(会导致程序终止),这就保障了所有挂起都是通过调用R的成员,这样库作者就能完全掌握挂起方式了。

不过这样的场景并不常见,他需要所有的挂起都通过库的特殊方式实现。

如下例所示:
buildSequence函数实现生成器时,必须保证携程中所有的挂起都是通过调用yield或者yieldAll()来实现。这就是为什么SequenceBuilder被标注为`@RestrictsSuspension

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐