//全局式. 协程的生命周期只受整个应用程序的生命周期的限制。
GlobalScope.launch(Dispatchers.Unconfined) {
println("hello, (${Thread.currentThread().name})")
delay(1000L)
println("stone")
}
// GlobalScope.async { }
//阻塞式
runBlocking {
delay(1000L) //与上一个delay基本是同时执行的,只晚一点点。
println("Did you win the lottery?")
}
//阻塞式 返回 T
val v = runBlocking {
delay(1500L)
GlobalScope.launch {
println("yeah. $2")
}
delay(500)
"i win"
}
println(v)
launch()
是 CoroutineScope
的扩展函数看源码,
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
内部会构建一个 coroutine
实例,并调用 start()
,来启动协程。返回 Job
实例。
async()
也是CoroutineScope
的扩展函数内部会构建一个 coroutine
实例,并调用 start()
,来启动协程。返回 Deferred
实例。
看函数名是异步的意思,但并不是说它就一定在异步线程中运行。而是其函数可能不是立即执行的。
runBlocking()
就是一个函数@Throws(InterruptedException::class)
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
val currentThread = Thread.currentThread()
val contextInterceptor = context[ContinuationInterceptor]
val eventLoop: EventLoop?
val newContext: CoroutineContext
if (contextInterceptor == null) {
// create or use private event loop if no dispatcher is specified
eventLoop = ThreadLocalEventLoop.eventLoop
newContext = GlobalScope.newCoroutineContext(context + eventLoop)
} else {
// See if context's interceptor is an event loop that we shall use (to support TestContext)
// or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }
?: ThreadLocalEventLoop.currentOrNull()
newContext = GlobalScope.newCoroutineContext(context)
}
val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
return coroutine.joinBlocking()
}
内部会构建一个 coroutine
实例,并调用 start()
,来启动协程。返回值是 传入的 block()
里的最后一行。所以,示例代码中,会输出 i win
。
这是阻塞式的实现,即 可以理解成是同步的,非异步的实现。阻塞的是调用者线程。
控制台程序中,若在某个非阻塞协程中使用 delay(),可能主函数 main()已经执行完了,而看不到后续的操作。所以,可以将main(),声明成:
fun main() = runBlocking { ... }
CoroutineScope
族谱
上图,是基于 kotlin.coroutines 1.3.5 的协程库(非安卓上的)中的 CoroutineScope
的族谱。
public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}
看源码中 CoroutineScope
的定义,内部定义了一个常量 coroutineContext
,实际上它是抽象的。如自定义一个 CoroutineScope
:
class MyContextScope(override val coroutineContext: CoroutineContext) : CoroutineScope {
}
结合上图,可知,sdk内部实现的这些
XxxCoroutine
协程,其实都实现了CoroutineScope
。
它们会对 coroutineContext 进行赋值处理。
coroutineScope()
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R =
suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
该sdk函数,是一个挂起函数,内部会创建一个 coroutine
协程实例。
因其是挂起函数,所以必须要被一个协程或另一个挂起函数调用。
fun main(args: Array<String>) = runBlocking {
coroutineScope {
launch {
delay(500L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before the nested launch
}
}
Job
、Deferred
Job
简介launch()
返回 Job
,其实现了 CoroutineContext
接口;内部有三个属性:
public val isActive: Boolean
public val isCompleted: Boolean
public val isCancelled: Boolean
文档注释中,有个如下简单的流程示意图,来描述了上面三个属性的作用:
wait children
+-----+ start +--------+ complete +-------------+ finish +-----------+
| New | -----> | Active | ---------> | Completing | -------> | Completed |
+-----+ +--------+ +-------------+ +-----------+
| cancel / fail |
| +----------------+
| |
V V
+------------+ finish +-----------+
| Cancelling | --------------------------------> | Cancelled |
+------------+ +-----------+
新的 Job 实例,启动后, 进入 Active 状态;
Active 状态的,可以进行取消操作,取消完成后,进入 Cancelled 状态;
Active 状态的,正常执行完成的(如果有子Job,等待所有子Job都执行完成),进入 Completed 状态;如果自身或子Job未执行完成时,被取消或发生了错误,进入 Cancelled 状态。
Job
重要的函数: cancel()
、join()
、cancelAndJoin
来个示例
val job = GlobalScope.launch {
delay(1000L)
launch {
delay(8000)
println(8/0)
}
println("World!")
}
job.cancel()
println("Hello,")
// job.join() // wait until child coroutine completes
println("unprecedented ${job.isCompleted}")
println("unprecedented ${job.isCancelled}")
cancel()
取消Job,其实就是取消了GlobalScope.launch{}
整体的协程任务。( delay(1000)之前有其它代码还是会执行的。 )job.isCompleted 值是 false
, job.isCancelled 值是 true
打开 job.join()
注释后,job.isCompleted 值是 true
, job.isCancelled 值是 true
。join()
是等待所有孩子都完成,所以其后,它的 isCompleted 是 true
。
cancelAndJoin()
内部实现就是先调用 cancel()
再调用 join()
对于 cancel() 并不是一调用就会立即取消的。它在遇到 会使协程 『挂起』的操作时,才能取消成功。
iii. Job
的子类Deferred
async()
返回 Deferred。它多了一个重要的方法 await()
。
// 挂起函数中,调用其它挂起函数;挂起协程
suspend fun doWorld() {
delay(1000L)
println("World!")
repeat(10) { i ->
print("${i}\t")
}
println()
}
//协程中 调用挂起函数
val deferred1 = async {
doWorld()
"done ${Thread.currentThread().name}"
}
println(deferred1.await())
val deferred2 = async(start = CoroutineStart.LAZY) {
println("时间:${System.currentTimeMillis()}")
"done ${Thread.currentThread().name}"
}
delay(3000)
println(deferred2.await())
async()
默认会立即执行,如 val r = async {...}
;调用 deferred1.await()
能获取到结果。
val deferred2 = async(start = CoroutineStart.LAZY) {...}
当 start 是 Lazy时,协程会在 调用 deferred2.await()
时才开始执行。
要开启怎样的一个协程:
GlobalScope 全局的,内部有线程池,可选 launch() 、 async()
非全局的,且不指定与异步线程相关的 CoroutineContext 参数时,默认就是执行在调用者线程中。
launch() 返回 Job, async() 返回 Deferred。根据是否要操作返回值,进行选择。
runBlocking() 会阻塞调用者线程,注意它需要一个返回值,即使是 Unit 的空类型。
挂起函数 coroutineScope() 能创建一个非全局的协程。
coroutines basics
kotlin 协程库