Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 每件事都是协同进行的。 挂起函数只是一个可以在以后暂停和恢复的函数。他们可以执行一个长时间运行的操作,并等待它完成而不阻塞_Kotlin_Async Await_Suspend_Kotlin Coroutines - Fatal编程技术网

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");
            }