Kotlin05基础

扩展函数(以及 lambda )可以被标记为 suspend 。这样方便了用户创建其他
DSLs 以及扩展其它 API 。有些情况下,库的作者需要阻止用户添加新的挂起线程的
方案。
这时就需要 @RestrictsSuspension 注解了。当一个接收者类或者接口 R 被标注
时,所有可挂起扩展都需要代理 R 的成员或者其它扩展。由于扩展时不能互相无
限代理(会导致程序终止),这就保障了所有挂起都是通过调用 R 的成员,这样
库作者就能完全掌控挂起方式了。
不过这样的场景不常见,它需要所有的挂起都通过库的特殊方式实现。比如,用下
面的 buildSequence() 函数实现生成器时,必须保证协程中所有的挂起都是通
过调用 yield() 或者 yieldAll() 来实现。这就是为什么 SequenceBuilder
被标注为 @RestrictsSuspension :
@RestrictsSuspension
public abstract class SequenceBuilder <in T> {
...
}
可以参看 Github 源码
协程内部机制
这里并不打算全盘解释协程内部的工作原理,而是给大家一个整体上的概念。
125 协程
协程完全时通过编译技术(并不需要 VM 或者 OS 方面的支持)实现,挂起时借由
代码转换实现。基本上所有的挂起函数(当然是有些优化措施,但这里我们不会深
入说明)都被转换为状态机。在挂起前,下一个状态会存储在编译器生成的与本地
变量关联的类中。到恢复协程时,本地变量会被恢复为挂起之前的状态。
挂起的协程可以存储以及作为一个对象进行传递,该协程会继续持有其状态和本地
变量。这样的对象的类型时 Continuation ,代码转换的整体实现思路是基于经
典的 Continuation-passing style 。所有挂起函数要有一个额外的参数类
Continuation
更多的细节可以参看 设计文档 。其它语言(比如 C# ECMASript2016 )中类似的
async/await 模型在这里都有描述,当然了其它语言的实现机制和 Kotlin 有所不同
协程的实验状态
协程的设计是 实验性 的,也就是说在后面的 releasees 版本中可能会有所变更。当
Kotlin1.1 中编译协程时,默认会有警告: The feature "coroutines" is
experimental 。可以通过 opt-in flag 来移除警告。
由于处于实验状态,协程相关的标准库都
kotlin.coroutines.experimental 包下。当设计确定时实验状态将会取消,
最后的 API 将会移到 kotlin.coroutines , 实验性的包将会保留(或许是作为一个
单独的构建中)以保持兼容。
千万注意 : 建议库作者可以采用同样的转换:为基于协程的 API 采用
"experimental" 前缀作包名(比如 com.example.experimental )。当最终 API
发布时,遵循下面的步骤:
复制所有 API com.example 包下
保留实验性大包做兼容。
这样可以减少用户的迁移问题。
底层 API kotlin.coroutines
底层 API 比较少,强烈建议不要使用,除非要创建高级库。这部分 API 主要在两个
包中:
kotlin.coroutines.experimental 带有主要类型与下述原语
createCoroutine()
startCoroutine()
suspendCoroutine()
kotlin.coroutines.experimental.intrinsics 带有更底层的内联函数如
suspendCoroutineOrReturn
关于这些 API 用法的更多细节可以在 这里 找到。
kotlin.coroutines 中的生成器 API
kotlin.coroutines.experimental 中唯一的 应用层面 的函数是:
buildSequence()
buildIterator()
这些和 kotlin-stdlib 打包在一起,因为和序列相关。事实上,这些函数(这
里单独以 buildSequence() 作为事例)实现生成器提供了一种更加简单的构造
延迟序列的方法:
val fibonacciSeq = buildSequence {
var a = 0
var b = 1
yield( 1 )
while ( true ) {
yield(a + b)
val tmp = a + b
a = b
b = tmp
}
}
这里通过调用 yield() 函数生成新的斐波那契数,就可以生成一个无限的斐波那
契数列。当遍历这样的数列时,每遍历一步就生成一个斐波那契数,这样就可以从
中取出无限的斐波那契数。比如 fibonacciSeq.take(8).toList() 会返回 [1,
1, 2, 3, 5, 8, 13, 21] 。协程让这一实现开销更低。
为了演示正真的延迟序列,在 buildSequence() 中打印一些调试信息:
val lazySeq = buildSequence {
print( "START " )
for (i in 1..5 ) {
yield(i)
print( "STEP " )
}
print( "END" )
}
// Print the first three elements of the sequence
lazySeq.take( 3 ).forEach { print( "$it " ) }
运行上面的代码运,如果我们输出前三个元素的数字与生成循环的 STEP 有交
叉。这意味着计算确实是惰性的。要输出 1 ,我们只执行到第一个
yield(i) ,并且过程中会输出 START 。然后,输出 2 ,我们需要继续下一个
128 协程
yield(i) ,并会输出 STEP 3 也一样。永远不会输出再下一个 STEP (以
END ),因为我们没有请求序列的后续元素。
使用 yieldAll() 函数可以一次性生成序列所有值:
val lazySeq = buildSequence {
yield( 0 )
yieldAll( 1..10 )
}
lazySeq.forEach { print( "$it " ) }
buildIterator() buildSequence() 作用相似,只不过返回值时延迟迭代
器。
通过给 SequenceBuilder 类写挂起扩展,可以给 buildSequence() 添加自定
义生成逻辑:
suspend fun SequenceBuilder<Int>. yieldIfOdd (x: Int) {
if (x % 2 != 0 ) yield(x)
}
val lazySeq = buildSequence {
for (i in 1..10 ) yieldIfOdd(i)
}
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值