我正在阅读Kotlin,并且知道它是基于suspend函数的。但是suspend是什么意思呢?
协同器还是函数被挂起?
来自https://kotlinlang.org/docs/reference/coroutines.html
基本上,coroutines是一种不阻塞线程就可以挂起的计算。
我经常听到人们说“暂停功能”。但我认为是协同线被暂停,因为它正在等待功能完成?“暂停”通常意味着“停止操作”,在这种情况下,协同线是空闲的。
我们应该说这条协同线被暂停了吗?
哪一种协同作用会被暂停?
来自https://kotlinlang.org/docs/reference/coroutines.html
要继续类推,await()可以是一个挂起函数(因此也可以从异步{}块中调用),它挂起一个协同线,直到完成某些计算并返回其结果:
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}它说“在计算完成之前暂停协同线”,但是coroutine就像一个轻量级线程。因此,如果协同线被挂起,如何进行计算?
我们看到await在computation上被调用,所以返回Deferred的可能是async,这意味着它可以启动另一个协同
fun computation(): Deferred<Boolean> {
return async {
true
}
}这句话说的是,它挂起一个协同线。它的意思是suspend外部async协同线,还是suspend内部computation协同线?
suspend是否意味着当外部async coroutine等待(await)内部computation coroutine完成时,它(外部async coroutine)空闲(因此名为async coroutine)并将线程返回到线程池,当子computation coroutine完成时,它(外部async coroutine)唤醒,从池中取出另一个线程并继续吗?
我提到这个线程的原因是因为https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
当协同线等待时,线程返回到池中,当等待完成时,协同线将在池中的一个空闲线程上恢复。
发布于 2018-10-22 08:23:26
挂起函数是一切协同的中心。挂起函数只是一个可以在以后暂停并恢复的函数。他们可以执行一个长时间运行的操作,并等待它完成而不阻塞。
除了添加suspend关键字之外,挂起函数的语法与常规函数的语法相似。它可以接受一个参数并具有一个返回类型。但是,挂起函数只能由另一个挂起函数或在协同线内调用。
suspend fun backgroundTask(param: Int): Int {
// long running operation
}在隐藏模式下,编译器将挂起函数转换为没有挂起关键字的另一个函数,该函数采用Continuation<T>类型的加法参数。例如,上面的函数将由编译器转换为:
fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
// long running operation
}Continuation<T>是一个接口,它包含两个函数,它们被调用以使用返回值恢复协同线,如果函数挂起时发生错误,则为异常。
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}发布于 2018-01-05 11:42:04
要理解挂起协同线的确切含义,我建议您通过以下代码:
import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
var continuation: Continuation<Int>? = null
fun main() {
GlobalScope.launch(Unconfined) {
val a = a()
println("Result is $a")
}
10.downTo(0).forEach {
continuation!!.resume(it)
}
}
suspend fun a(): Int {
return b()
}
suspend fun b(): Int {
while (true) {
val i = suspendCoroutine<Int> { cont -> continuation = cont }
if (i == 0) {
return 0
}
}
}Unconfined协同调度器消除了协同调度的魔力,并允许我们直接关注裸协同。
launch块中的代码作为launch调用的一部分立即开始在当前线程上执行。所发生的情况如下:
val a = a()b(),到达suspendCoroutine。b()执行传递给suspendCoroutine的块,然后返回一个特殊的COROUTINE_SUSPENDED值。通过Kotlin编程模型无法观察到这个值,但这正是编译后的Java方法所做的。a(),看到这个返回值,它本身也会返回它。launch块执行相同的操作,现在控件在launch调用之后返回到行:10.downTo(0)...请注意,此时,您的效果与launch块中的代码和fun main代码同时执行的效果相同。这一切发生在一个本地线程上,因此launch块被“挂起”。
现在,在forEach循环代码中,程序读取b()函数编写的continuation,并使用10值对其进行resumes。resume()的实现方式就像用传入的值返回suspendCoroutine调用一样。因此,您突然发现自己正在执行b()。传递给resume()的值被分配给i,并根据0进行检查。如果不是零,则while (true)循环在b()中继续进行,再次到达suspendCoroutine,此时您的resume()调用将返回,现在您将在forEach()中执行另一个循环步骤。这种情况一直持续到最后继续使用0,然后运行println语句并完成程序。
上面的分析应该给您一个重要的直觉,即“挂起协同线”意味着将控件返回到最内部的launch调用(或者更广泛地说,是coroutine构建器)。如果协同线在恢复后再次挂起,则resume()调用结束并控制返回给resume()的调用方。
协同调度器的存在使这一推理变得不那么清晰,因为它们中的大多数立即将您的代码提交到另一个线程。在这种情况下,上面的故事发生在另一个线程中,coroutine还管理continuation对象,以便在返回值可用时恢复它。
发布于 2019-12-16 09:41:28
由于已有许多好的答案,我想为其他人举一个简单的例子。
runBlocking用例:
suspend函数runBlocking { }以阻塞方式启动Coroutine。这类似于我们如何使用Thread类阻塞普通线程,并在某些事件发生后通知阻塞线程。runBlocking { } 阻塞当前执行线程,直到协同线({}之间的主体)完成为止。
覆盖onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) Log.i(标记,“外部代码开始于线程:”+ Thread.currentThread().name);runBlocking {Log.d(标记,“内部代码开始于线程:”+ Thread.currentThread().name +“使外部代码挂起”);myMethod();}Log.i(标记,“外部代码在线程上继续进行:”+ Thread.currentThread().name);}私有挂起的myMethod() { withContext(Dispatchers.Default) { for(i in 1..5) {Log.d(标记,“内部代码i:线程上的$i:”+ Thread.currentThread().name);}}这一产出如下:
I/TAG: Outer code started on Thread : main
D/TAG: Inner code started on Thread : main making outer code suspend
// ---- main thread blocked here, it will wait until coroutine gets completed ----
D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- main thread resumes as coroutine is completed ----
I/TAG: Outer code resumed on Thread : main启动用例:
launch { }同时启动一个协同线。worker线程上开始执行。worker线程和外部线程(我们称之为launch { })都同时运行。在内部,JVM可以执行抢占式线程。scopes。如果我们指定GlobalScope,协同线将一直工作到应用程序生存期结束。
覆盖onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) Log.i(标记,“外部代码开始于线程:”+ Thread.currentThread().name);GlobalScope.launch(Dispatchers.Default) {Log.d(标记,“内部代码开始于线程:”+ Thread.currentThread().name +“使外部代码挂起”);myMethod();}Log.i(标记,“外部代码在线程上继续进行:”+ Thread.currentThread().name);}私有挂起的myMethod() { withContext(Dispatchers.Default) { for(i in 1..5) {Log.d(标记,“内部代码i:线程上的$i:”+ Thread.currentThread().name);}这一产出如下:
10806-10806/com.example.viewmodelapp I/TAG: Outer code started on Thread : main
10806-10806/com.example.viewmodelapp I/TAG: Outer code resumed on Thread : main
// ---- In this example, main had only 2 lines to execute. So, worker thread logs start only after main thread logs complete
// ---- In some cases, where main has more work to do, the worker thread logs get overlap with main thread logs
10806-10858/com.example.viewmodelapp D/TAG: Inner code started on Thread : DefaultDispatcher-worker-1 making outer code suspend
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-1异步和等待用例:
async和await会提供帮助。2挂起函数myMethod()和myMethod2()。myMethod2()只有在完全完成myMethod() 或 myMethod2()后才能执行,这取决于myMethod()的结果,我们可以使用async和await。async并行地启动一个协同线,类似于launch。但是,它提供了一种在并行启动另一个协同线之前等待一个协同线的方法。await()。async返回Deffered<T>的一个实例。默认情况下,T将是Unit。当我们需要等待任何async的完成时,我们需要对该async的Deffered<T>实例调用.await()。与下面的示例一样,我们调用了innerAsync.await(),这意味着执行将被暂停,直到innerAsync完成为止。我们可以观察到相同的输出。innerAsync先完成,然后调用myMethod()。然后再调用async innerAsync2,它调用myMethod2()。
覆盖onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) Log.i(标记,“外部代码开始于线程:”+ Thread.currentThread().name);job = GlobalScope.launch(Dispatchers.Default) { innerAsync =异步{Log.d(标记,“内部代码开始于线程:”+ Thread.currentThread().name +“使外部代码挂起”);myMethod();} innerAsync.await() innerAsync2 =异步{Log.w(标记,“内部代码从线程开始:”+ Thread.currentThread().name +“使外部代码挂起”);myMethod2();}Log.i(标记,“在线程上恢复外部代码:”+ Thread.currentThread().name);}私有挂起myMethod() { withContext(Dispatchers.Default) { for(i in 1..5) {Log.d(标记,“内部代码i:线程上的$i:”+ Thread.currentThread().name);}私有挂起myMethod2() { withContext(Dispatchers.Default) { for(i in 1..10) {Log.w(标记,“内部代码i:线程上的$i:”+ Thread.currentThread().name);}这一产出如下:
11814-11814/? I/TAG: Outer code started on Thread : main
11814-11814/? I/TAG: Outer code resumed on Thread : main
11814-11845/? D/TAG: Inner code started on Thread : DefaultDispatcher-worker-2 making outer code suspend
11814-11845/? D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- Due to await() call, innerAsync2 will start only after innerAsync gets completed
11814-11848/? W/TAG: Inner code started on Thread : DefaultDispatcher-worker-4 making outer code suspend
11814-11848/? W/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 6 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 7 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 8 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 9 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 10 on Thread : DefaultDispatcher-worker-4https://stackoverflow.com/questions/47871868
复制相似问题