在kotlin1.1,Coroutines還處於實驗階段。
有些API啓動耗時操作比如:網絡IO,文件流IO,CPU/GPU要求高的工作,它們會導致調用阻塞直到操作完成。Coroutines通過一種更便宜,更容易控制的suspension(掛起)操作,來避免線程阻塞。
Coroutines把複雜的運算移到了libraries,因而簡化了異步編程。在Coroutine中,程序邏輯可以順序表達(同步),底層的libraries可以幫我們完成異步操作,我們只需要在異步回調中編寫我們的代碼就完成了,可以在不同的線程訂閱相關的事情,執行計劃,代碼就像順序執行一樣簡單。
像其他語言中的大多數異步機制都可以通過coroutines實現。包括C#和ECMAScript的async/await,Go的channels和select, C#和Python的generator/yeild.
阻塞VS掛起
基本上,coroutines可以掛起運算而不會阻塞線程。阻塞線程通常代價昂貴,特別是在高負載的情況下,因爲在真實的情境下,僅僅會很少的幾個線程會在運行,所以阻塞其中一個線程會導致很多重要的工作會被延遲。
換句話說,Coroutine基本上是無代價的。不需要切換上下文,也不需要任何OS的參與。在這基礎上,suspension可以在很大程度上被使用者library控制,作爲一個library的開發者,我們可以決定suspension的時候做什麼,比如優化/打印/攔截,完全可以根據我們自己的需要。
不同的地方是,Coroutines不能被隨時掛起,僅僅是在調用函數的時候。
通過suspend定義一個掛起函數
suspend fun doSomething(foo: Foo): Bar {
...
}
在kotlinx.coroutines中有
fun <T> async(block: suspend () -> T)
所以我們可以如下調用
async {
doSomething(foo)
...
}
但是我們需要注意,不能在一個普通的函數如main()中調用掛起函數
fun main(args: Array<String>) {
doSomething() // ERROR: Suspending function called from a non-coroutine context
}
而且掛起函數可以是虛函數,可以被子類覆寫
interface Base {
suspend fun foo()
}
class Derived: Base {
override suspend fun foo() { ... }
}
使用@RestrictsSuspension註釋可以使用者定義新的掛起方式,最經典的例子是
@RestrictsSuspension
@SinceKotlin("1.1")
public abstract class SequenceBuilder<in T> internal constructor() {
/**
* Yields a value to the [Iterator] being built.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
* @sample samples.collections.Sequences.Building.buildFibonacciSequence
*/
public abstract suspend fun yield(value: T)
/**
* Yields all values from the `iterator` to the [Iterator] being built.
*
* The sequence of values returned by the given iterator can be potentially infinite.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
*/
public abstract suspend fun yieldAll(iterator: Iterator<T>)
/**
* Yields a collections of values to the [Iterator] being built.
*
* @sample samples.collections.Sequences.Building.buildSequenceYieldAll
*/
public suspend fun yieldAll(elements: Iterable<T>) {
if (elements is Collection && elements.isEmpty()) return
return yieldAll(elements.iterator())
}
因爲SequenceBuilder類的成員函數都是掛起函數,設計者不希望使用者添加新的掛起函數,所以可以使用@RestrictsSuspension做了限制。
Coroutines的內部工作機制,這個比較複雜,使用是的編譯器技巧,有興趣可以看英文資料Coroutines
需要注意是的,Coroutines目前還處於實驗階段。