Kotlin 每件事都是协同进行的。 挂起函数只是一个可以在以后暂停和恢复的函数。他们可以执行一个长时间运行的操作,并等待它完成而不阻塞
挂起函数的语法与常规函数的语法相似,只是添加了Kotlin 每件事都是协同进行的。 挂起函数只是一个可以在以后暂停和恢复的函数。他们可以执行一个长时间运行的操作,并等待它完成而不阻塞,kotlin,async-await,suspend,kotlin-coroutines,Kotlin,Async Await,Suspend,Kotlin Coroutines,挂起函数的语法与常规函数的语法相似,只是添加了suspend关键字。它可以接受参数并具有返回类型。但是,挂起函数只能由另一个挂起函数或在协同程序中调用 suspend fun backgroundTask(param: Int): Int { // long running operation } 在后台,编译器会将suspend函数转换为另一个不带suspend关键字的函数,该函数使用类型为Continuation的附加参数。例如,上面的函数将由编译器转换为: fun backgr
suspend
关键字。它可以接受参数并具有返回类型。但是,挂起函数只能由另一个挂起函数或在协同程序中调用
suspend fun backgroundTask(param: Int): Int {
// long running operation
}
在后台,编译器会将suspend函数转换为另一个不带suspend关键字的函数,该函数使用类型为Continuation
的附加参数。例如,上面的函数将由编译器转换为:
fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
// long running operation
}
有趣的背景任务(参数:Int,回调:Continuation):Int{
//长时间运行
}
Continuation
是一个包含两个函数的接口,如果函数挂起时发生错误,则调用这两个函数以使用返回值或异常恢复协同程序
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
接口延续{
val上下文:CoroutineContext
趣味简历(值:T)
有趣的简历例外(例外:可丢弃)
}
首先,了解本IMO的最佳来源是Roman Elizarov的演讲
协同程序或函数被挂起
调用suspending函数suspends协程,这意味着当前线程可以开始执行另一个协程。因此,协同程序被认为是暂停的,而不是函数
事实上,由于这个原因,挂起函数的调用位置被称为“挂起点”
哪个协同程序被暂停
让我们看看您的代码,并分析发生了什么:
//1。这个调用将启动一个新的协程(我们称之为C1)。
//如果后面有代码,它将与
//此异步文件的正文
异步的{
...
//2.这是一个常规函数调用
val deferred=计算()
//4.因为wait()是挂起的,所以它挂起了协程C1。
//这意味着如果调度程序中只有一个线程,
//现在可以自由执行C2了
//7.一旦C2完成,C1将恢复,C2的异步结果为'true'
val result=deferred.await()
...
//8.C1现在可以在当前线程中继续运行,直到
//再次暂停(或不暂停)
}
乐趣计算():延迟{
//3.此异步调用启动第二个协同路由(C2)。具体取决于
//您正在使用的调度程序可能有一个或多个线程。
//3.a.如果您有多个线程,则此异步的块可能是
//与另一个线程中的C1并行执行。控制流
//当前线程的调用方返回计算()的调用方。
//3.b.如果只有一个线程,则该块有点“排队”,但
//不立即执行,控制流返回到
//计算()的调用方(除非有特殊的调度程序或
//使用了coroutine start参数,但让我们保持简单)。
//在这两种情况下,我们都说这个块“并发”执行
//用C1。
异步返回{
//5.现在可以执行此操作
真的
//6.C2现在已完成,因此线程可以返回执行
//另一个协同程序(例如,此处为C1)
}
}
外部async
启动协同程序。当调用computation()
时,内部async
启动第二个协同路由。然后,对await()
的调用将暂停外部async
协同路由的执行,直到内部async
协同路由的执行结束
您甚至可以在单个线程中看到这一点:线程将执行外部async
的开头,然后调用computation()
并到达内部async
。此时,跳过内部异步的主体,线程继续执行外部async
,直到到达await()
。
await()
是一个“暂停点”,因为await
是一个暂停函数。
这意味着外部协程被挂起,因此线程开始执行内部协程。完成后,它返回执行外部async
的末尾
suspend是否意味着当外部异步协程等待(等待)内部计算协程完成时,它(外部异步协程)将空闲(因此命名为suspend)并将线程返回到线程池,当子计算协程完成时,它(外部异步协程)将被唤醒,从池中获取另一个线程并继续
没错
实际实现的方法是将每个挂起函数转换为状态机,其中每个“状态”对应于该挂起函数内的挂起点。在引擎盖下,该函数可以被多次调用,其中包含关于它应该从哪个暂停点开始执行的信息(您应该真正地观看我链接的视频,以了解更多关于该点的信息)。我想给您一个简单的延续概念示例。这就是挂起函数所做的,它可以冻结/挂起,然后继续/恢复。不要再考虑线程和信号量方面的协同程序。从延续甚至回调钩子的角度考虑 为了清楚起见,可以使用
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
想象一下它是这样工作的:
因此,您从中启动的当前函数不会停止,只是一个协同程序会在继续时挂起。运行suspend函数不会暂停线程
我想你把事情说清楚了,是我的推荐人
interface Continuation<in T> {
val context: CoroutineContext
fun resume(value: T)
fun resumeWithException(exception: Throwable)
}
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
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
}
}
}
}
override fun onResume() {
super.onResume()
unFreeze()
}
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);
}
}
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
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);
}
}
}
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
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);
}
}
}
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
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)
@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");
}