Coroutine scope负责在不同的coroutine之间形成结构化和父-子关系(这里有点抽象,稍后会解释什么是结构化和父-子关系),通常会从scope中启动一个新的coroutine。Coroutine context中保存了运行coroutine需要的信息,比如coroutine运行的线程等。
通常使用launch
,async
,runBlocking
启动新的coroutine,这三个会自动创建对定的scope。这三个方法都会接受一个lambda作为参数,隐式的receiver接受类型是CoroutineScope
:
launch { /* this: CoroutineScope */
}
新的coroutine只可以在scope中被启动。launch
和async
是CoroutineScope
的扩展方法,所以在调用他们之前必须显式或隐式地传递receiver。但是runBlocking
中的coroutine是唯一的例外:runBlocking
是一个top-level函数。但是因为runBlocking
会阻塞当前线程,所以主要被用在main方法或者测试中作为一个桥梁方法。在launch
,async
或者runBlocking
里启动的coroutine会自动的从属于对应的scope。
fun main() = runBlocking { /* this: CoroutineScope */
launch { /* ... */ }
// the same as:
this.launch { /* ... */ }
}
在runBlocking
中调用launch
时会其实是隐式的调用CoroutineScope.launch
,这和下面this.launch
是等价的。
外面coroutine(runBlocking
)和内部嵌套调用的coroutine(上面的launch
)形成了父-子关系。
在不启动coroutine的情况下也可以创建新的scope,通过coroutineScope
可以做到这一点。在不访问外部scope的情况下在suspend方法中创建新的coroutine,这个coroutine会自动的成为外部调用这个suspend方法所在的scope形成父-子关系。
使用GlobalScope.async
或者GlobalScope.launch
也可以创建全局scope的top-level 的coroutine。
上面说的就是父-子关系,提供结构化的机制称为“结构化并发(structured concurrency)”,这样的机制有以下优点:
- scope可以控制所有子coroutine的生命周期
- scope可以取消所有的子coroutine
- scope会等待所有的子coroutine任务完成
但是如果使用的是上面提到的GlobalScope.async
来启动coroutine就会破坏这种结构化的关系。因为通过GlobalScope.async
启动的coroutine是完全独立的,和应用的生命周期绑定。
网友评论