挂起函数在Kotlin协程中意味着什么?

我正在阅读Kotlin协程,并知道它是基于suspend函数。但是suspend是什么意思呢?

协程或函数被挂起?

https://kotlinlang.org/docs/reference/coroutines.html

基本上,协程是可以挂起而不阻塞线程的计算

我经常听到人们说“暂停功能”。但我认为是协程被挂起,因为它正在等待函数完成?“suspend"通常表示“停止操作”,在这种情况下,协程是空闲的。

我们是否应该说协程被挂起?

哪个协程被挂起?

https://kotlinlang.org/docs/reference/coroutines.html

继续类比,await()可以是一个挂起函数(因此也可以从async{}块中调用),它挂起协程,直到完成一些计算并返回其结果:

async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}

它说“暂停一个协程,直到完成一些计算”,但协程就像一个轻量级线程。那么,如果协程挂起,如何进行计算呢?

我们看到awaitcomputation上被调用,所以返回Deferred的可能是async,这意味着它可以启动另一个协程

fun computation(): Deferred<Boolean> {
return async {
true
}
}

引用说挂起协程。它是指suspend外部的async协程,还是suspend内部的computation协程?

suspend是否意味着当外部async协程正在等待(await)内部computation协程完成时,它(外部async协程)空闲(因此称为suspend)并将线程返回线程池,当子computation协程完成时,它(外部async协程)醒来,从线程池中取出另一个线程并继续?

我之所以提到线程是因为https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html

当协程正在等待时,线程被返回到池中,当等待完成时,协程在池中的空闲线程上恢复

165156 次浏览

为了理解挂起协程到底意味着什么,我建议你看一下下面的代码:

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调用的一部分。具体情况如下:

  1. 评估val a = a()
  2. 这个链到b(),到达suspendCoroutine
  3. 函数b()执行传递给suspendCoroutine的块,然后返回一个特殊的COROUTINE_SUSPENDED值。通过Kotlin编程模型无法观察到这个值,但这正是编译后的Java方法所做的。
  4. 函数a(),看到这个返回值,本身也返回它。
  5. launch块做同样的事情,现在控制返回到launch调用之后的行:10.downTo(0)...

注意,在这一点上,如果launch块中的代码和你的fun main代码并发执行,效果是一样的。恰好所有这些都发生在单个本机线程上,因此launch块被“挂起”。

现在,在forEach循环代码中,程序读取b()函数编写的continuation,并将其值10 resumesresume()以这样一种方式实现,就好像suspendCoroutine调用返回了你传入的值。所以你突然发现自己正在执行b()。传递给resume()的值被赋值给i,并根据continuation0进行检查。如果它不为零,continuation1循环在b()内部继续,再次到达suspendCoroutine,此时你的resume()调用返回,现在你在continuation5中经历另一个循环步骤。这个过程会一直持续下去,直到最后你用continuation0继续,然后continuation7语句运行,程序完成。

上面的分析应该给您一个重要的直观印象,即“暂停一个协程”;意味着将控件返回到最里面的launch调用(或更一般的协同程序生成器)。如果协程在恢复后再次挂起,resume()调用结束,控制权返回给resume()的调用者。

协程分派器的存在使得这种推理不那么清晰,因为它们中的大多数会立即将您的代码提交给另一个线程。在这种情况下,上面的故事发生在另一个线程中,协程调度程序还管理continuation对象,以便在返回值可用时恢复它。

暂停功能是所有协程的中心。 挂起函数只是一个可以在以后暂停和恢复的函数。它们可以执行一个长时间运行的操作,并等待它完成而不阻塞

挂起函数的语法与常规函数相似,只是增加了suspend关键字。它可以接受一个参数并具有返回类型。但是,挂起函数只能由另一个挂起函数或在协程中调用。

suspend fun backgroundTask(param: Int): Int {
// long running operation
}

在底层,挂起函数由编译器转换为另一个不带suspend关键字的函数,该函数接受类型为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)
}

但是暂停是什么意思呢?

suspend关键字标记的函数在编译时被转换为异步的(在字节码中),即使它们在源代码中看起来是同步的。

在我看来,理解这种转变的最好来源是Roman Elizarov的谈话“深入coroutines”;

例如,下面的函数:

class MyClass {
suspend fun myFunction(arg: Int): String {
delay(100)
return "bob"
}
}

转换为以下(为简单起见,用Java而不是实际的JVM字节码表示):

public final class MyClass {
public final Object myFunction(int arg, @NotNull Continuation<? super String> $completion) {
// ...
}
}

这包括对函数的以下更改:

  • 返回类型被更改为Java的Object(相当于Kotlin的Any? -一个包含所有值的类型),以允许返回一个特殊的COROUTINE_SUSPENDED令牌来表示协程实际挂起的时间
  • 它得到一个额外的Continuation<X>参数(其中X是在代码中声明的函数的前一个返回类型-在示例中是String)。在恢复suspend函数时,这种延续就像一个回调。
  • 它的主体被转换为一个状态机(而不是为了提高效率而直接使用回调)。这是通过将函数体分解为悬挂点周围的部分,并将这些部分转换为一个大开关的分支来实现的。局部变量的状态以及我们在开关中的位置存储在Continuation对象中。

这是一种非常快速的描述方式,但是你可以在演讲中看到更多的细节和例子。这整个转变基本上是如何“暂停/恢复”;机制是在底层实现的。

协程或函数被挂起?

在高层次上,我们说调用一个suspend荷兰国际集团(ing)函数suspend<年代trong>年代为协程,这意味着当前线程可以开始执行另一个协程。因此,协同程序被说成是挂起的,而不是函数。

事实上,暂停函数的调用点被称为“暂停点”。因为这个原因。

哪个协程被挂起?

让我们看看你的代码,并分解发生了什么(编号遵循执行时间轴):

// 1. this call starts a new coroutine (let's call it C1).
//    If there were code after it, it would be executed concurrently with
//    the body of this async
async {
...
// 2. this is a regular function call, so we go to computation()'s body
val deferred = computation()
// 4. because await() is suspendING, it suspends coroutine C1.
//    This means that if we had a single thread in our dispatcher,
//    it would now be free to go execute C2
// 7. once C2 completes, C1 is resumed with the result `true` of C2's async
val result = deferred.await()
...
// 8. C1 can now keep going in the current thread until it gets
//    suspended again (or not)
}


fun computation(): Deferred<Boolean> {
// 3. this async call starts a second coroutine (C2). Depending on the
//    dispatcher you're using, you may have one or more threads.
// 3.a. If you have multiple threads, the block of this async could be
//      executed in parallel of C1 in another thread
// 3.b. If you have only one thread, the block is sort of "queued" but
//      not executed right away (as in an event loop)
//
//    In both cases, we say that this block executes "concurrently"
//    with C1, and computation() immediately returns the Deferred
//    instance to its caller (unless a special dispatcher or
//    coroutine start argument is used, but let's keep it simple).
return async {
// 5. this may now be executed
true
// 6. C2 is now completed, so the thread can go back to executing
//    another coroutine (e.g. C1 here)
}
}

外层async开始一个协程。当它调用computation()时,内部async启动第二个协程。然后,对await()的调用将挂起 async协程的执行,直到内心的 async的协程执行结束。

你甚至可以用单个线程看到:线程将执行外部async的开始,然后调用computation()并到达内部async。在这一点上,内部异步体被跳过,线程继续执行外部async,直到到达await()await()是一个“暂停点”,因为await是一个暂停函数。 这意味着外部协程被挂起,因此线程开始执行内部协程。当它完成时,返回来执行外层async的结束

挂起是否意味着当外部异步协程正在等待(await)内部计算协程完成时,它(外部异步协程)空闲(因此称为挂起)并返回线程池,当子计算协程完成时,它(外部异步协程)醒来,从池中取出另一个线程并继续?

是的,准确。

这实际上是通过将每个暂停函数转换为一个状态机来实现的,其中每个“状态”;对应于此挂起函数内的挂起点。在底层,函数可以被多次调用,并带有关于它应该从哪个挂起点开始执行的信息(您应该真正地观看我链接的视频以获得有关这方面的更多信息)。

我想给你们举一个关于延续概念的简单例子。这就是挂起函数所做的,它可以冻结/挂起,然后继续/恢复。不要从线程和信号量的角度来考虑协程。从延续甚至回调钩子的角度来考虑。

需要明确的是,协程可以通过使用suspend函数暂停。让我们来研究一下:

在android中,我们可以这样做,例如:

var TAG = "myTAG:"
fun myMethod() { // function A in image
viewModelScope.launch(Dispatchers.Default) {
for (i in 10..15) {
if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
freezePleaseIAmDoingHeavyWork()
} else
println("$TAG $i")
}
}


//this area is not suspended, you can continue doing work
}




suspend fun freezePleaseIAmDoingHeavyWork() { // function B in image
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)//delay pauses coroutine, NOT the thread. use  Thread.sleep if you want to pause a thread.
}
println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
}
}
}

上面的代码输出如下:

I: myTAG: my coroutine is frozen but i can carry on to do other things


I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done


I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10


I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume


I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15

想象一下它是这样工作的:

enter image description here

所以你启动的当前函数不会停止,只有协程会在它继续运行时挂起。线程不会通过运行挂起函数暂停。

我认为这个网站可以提供帮助你直截了当的事情,是我的参考。

让我们做一些很酷的事情,在迭代过程中冻结我们的挂起函数。我们将在后面的onResume中恢复它

存储一个名为continuation的变量,我们将用协程延续对象加载它:

var continuation: CancellableContinuation<String>? = null


suspend fun freezeHere() = suspendCancellableCoroutine<String> {
continuation = it
}


fun unFreeze() {
continuation?.resume("im resuming") {}
}

现在,让我们回到挂起的函数,让它在迭代过程中冻结:

 suspend fun freezePleaseIAmDoingHeavyWork() {
withContext(Dispatchers.Default) {
async {
//pretend this is a big network call
for (i in 1..10) {
println("$TAG $i")
delay(1_000)
if(i == 3)
freezeHere() //dead pause, do not go any further
}
}
}
}

然后在其他地方,如onResume(例如):

override fun onResume() {
super.onResume()
unFreeze()
}

循环将继续。我们可以在任何时候冻结一个挂起函数,并在经过一段时间后恢复它。你也可以查看渠道

我发现理解suspend的最好方法是在this关键字和coroutineContext属性之间做一个类比。

Kotlin函数可以声明为本地函数或全局函数。局部函数可以神奇地访问this关键字,而全局函数不能。

Kotlin函数可以声明为suspend或blocking。suspend函数神奇地可以访问coroutineContext属性,而阻塞函数则不能。

问题是:coroutineContext属性 是否像“正常”属性一样声明在Kotlin stdlib,但这个声明只是一个存根文档/导航的目的。事实上,coroutineContext内在属性,这意味着在底层编译器魔法意识到这个属性,就像它意识到语言关键字一样

this关键字对局部函数的作用与coroutineContext属性对suspend函数的作用相同:它提供了对当前执行上下文的访问。

因此,你需要suspend来访问coroutineContext属性——当前执行的协程上下文的实例

既然已经有很多好的答案了,我想给其他人一个更简单的例子。

runBlocking用例:

  • myMethod()是suspend函数
  • runBlocking { }以阻塞方式启动协程。这类似于我们用Thread类阻塞普通线程,并在某些事件发生后通知阻塞的线程。
  • runBlocking { }执行当前正在执行的线程,直到协程(body between {})完成

     override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)
    Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    runBlocking {
    Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
    myMethod();
    }
    Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    
    private suspend fun myMethod() {
    withContext(Dispatchers.Default) {
    for(i in 1..5) {
    Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
    }
    }
    

This outputs :

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,协程将一直工作到应用程序生命周期结束。

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)
    Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
    
    GlobalScope.launch(Dispatchers.Default) {
    Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
    myMethod();
    }
    Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    
    private suspend fun myMethod() {
    withContext(Dispatchers.Default) {
    for(i in 1..5) {
    Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
    }
    }
    }
    

This Outputs :

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

异步等待用例:

  • 当我们有多个任务要做而且时,它们取决于其他人的完成,asyncawait会有所帮助。
  • 例如,在下面的代码中,有2挂起函数myMethod()和myMethod2()。myMethod2()只能在myMethod()完全完成后执行。 myMethod2()取决于myMethod()的结果,我们可以使用asyncawait
  • async启动一个类似于launch的并行协程。但是,它提供了一种在并行启动另一个协程之前等待一个协程的方法。
  • 这种方式是await()async返回Deffered<T>的一个实例。T默认为Unit。当我们需要等待任何async的完成时,我们需要在该asyncDeffered<T>实例上调用.await()。就像下面的例子,我们调用innerAsync.await(),这意味着执行将被暂停,直到async0完成。我们可以在输出中观察到同样的情况。async0首先完成,它调用async2。然后下一个async async4启动,它调用async5

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_activity)
    Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
    
    job = GlobalScope.launch(Dispatchers.Default) {
    innerAsync = async {
    Log.d(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
    myMethod();
    }
    innerAsync.await()
    
    
    innerAsync2 = async {
    Log.w(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
    myMethod2();
    }
    }
    
    
    Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    
    private suspend fun myMethod() {
    withContext(Dispatchers.Default) {
    for(i in 1..5) {
    Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
    }
    }
    }
    
    
    private suspend fun myMethod2() {
    withContext(Dispatchers.Default) {
    for(i in 1..10) {
    Log.w(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
    }
    }
    }
    

This outputs :

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-4

这里有很多很好的答案,但我认为还有两件重要的事情需要注意。

例子中的launch / withContext / runBlocking和其他很多东西都来自协程库。其实和暂停没有任何关系。使用协程不需要协程库。协程是编译器的“诡计”。是的,标准库确实让事情变得更简单,但编译器正在执行挂起&恢复的事情。

第二件事,编译器只是把看起来是过程的代码转换成内部的回调。

取以下最小协程,挂起不使用协程库的协程:

lateinit var context: Continuation<Unit>


suspend {
val extra="extra"
println("before suspend $extra")
suspendCoroutine<Unit> { context = it }
println("after suspend $extra")
}.startCoroutine(
object : Continuation<Unit> {
override val context: CoroutineContext = EmptyCoroutineContext
// called when a coroutine ends. do nothing.
override fun resumeWith(result: Result<Unit>) {
result.onFailure { ex : Throwable -> throw ex }
}
}
)


println("kick it")
context.resume(Unit)

我认为理解它的一个重要方法是看看编译器对这些代码做了什么。实际上,它为lambda创建了一个类。它在类中为“extra”创建了一个属性;字符串,然后它创建两个函数,一个输出“before”;另一个是打印“after”。

实际上,编译器将看起来像过程代码的代码转换为回调。

那么suspend关键字是做什么的呢?它告诉编译器要回溯到多远的地方去寻找生成的回调所需要的上下文。编译器需要知道哪个变量在哪个“回调”中被使用,而suspend关键字可以帮助它。在这个例子中,“extra”;变量在挂起之前和之后都使用。因此,它需要被拉出到包含编译器所做回调的类的属性中。

它还告诉编译器这是“开始”;状态,并准备将以下代码拆分为回调。startCoroutine只存在于挂起lambda上。

Kotlin编译器生成的实际Java代码在这里。这是一个switch语句而不是回调,但实际上是一样的。首先调用w/ case 0,然后在恢复后调用w/ case 1。

            @Nullable
public final Object invokeSuspend(@NotNull Object $result) {
var10_2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0: {
ResultKt.throwOnFailure((Object)$result);
extra = "extra";
var3_4 = "before delay " + extra;
var4_9 = false;
System.out.println((Object)var3_4);
var3_5 = this;
var4_9 = false;
var5_10 = false;
this.L$0 = extra;
this.L$1 = var3_5;
this.label = 1;
var5_11 = var3_5;
var6_12 = false;
var7_13 = new SafeContinuation(IntrinsicsKt.intercepted((Continuation)var5_11));
it = (Continuation)var7_13;
$i$a$-suspendCoroutine-AppKt$main$1$1 = false;
this.$context.element = it;
v0 = var7_13.getOrThrow();
if (v0 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
DebugProbesKt.probeCoroutineSuspended((Continuation)var3_5);
}
v1 = v0;
if (v0 == var10_2) {
return var10_2;
}
** GOTO lbl33
}
case 1: {
var3_6 = this.L$1;
extra = (String)this.L$0;
ResultKt.throwOnFailure((Object)$result);
v1 = $result;
lbl33:
// 2 sources


var3_8 = "after suspend " + extra;
var4_9 = false;
System.out.println((Object)var3_8);
return Unit.INSTANCE;
}
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}

对于那些仍然想知道如何挂起一个挂起函数的人,我们在挂起函数体中使用了suspendCoroutine函数。

    suspend fun foo() :Int
{
Log.d(TAG,"Starting suspension")
return suspendCoroutine<Int> { num->


val result = bar()
Log.d(TAG,"Starting resumption")
num.resumeWith(Result.success(result))
}


}


fun bar():Int //this is a long runnning task

假设我们有一个名为myFunction的函数。

fun myFunction(){
Code block 1
Code block 2 //this one has a long running operation
Code block 3
Code block 4
}

通常这些代码块像block1、block2、block3、block4那样执行。因此,代码块3和4可能在代码块2仍在运行时执行。因为这个原因,就会出现问题。(屏幕可能冻结,应用程序可能崩溃)

但是如果我们让这个函数挂起

suspend fun MyFunction(){
Code block 1
Code block 2 //this one has a long running operation
Code block 3
Code block 4
}

现在,该函数可以在代码块2(长时间运行的操作)开始执行时暂停,并在完成时恢复。代码块3和4将在此之后执行。因此不会出现意外的线程共享问题。