網(wǎng)站首頁 編程語言 正文
學(xué)習(xí)了極客時間課程,記錄下學(xué)習(xí)輸出。
一、CPS轉(zhuǎn)換
掛起函數(shù),比普通的函數(shù)多了 suspend 關(guān)鍵字。通過suspend 關(guān)鍵字,Kotlin 編譯器就會特殊對待這個函數(shù),將其轉(zhuǎn)換成一個帶有 Callback 的函數(shù),這里的 Callback 就是 Continuation 接口。
例、CPS 轉(zhuǎn)換:
suspend fun getUserInfo(): Any {
return "UserInfo"
}
----->
fun getUserInfo(ct:Continuation): Any? {
ct.resumeWith("UserInfo")
return Unit
}
PS 轉(zhuǎn)換過程中,函數(shù)的類型發(fā)生了變化:suspend ()->Any 變成了 (Continuation)-> Any?。這意味著,如果你在 Java 里訪問一個 Kotlin 掛起函數(shù) getUserInfo(),會看到 getUserInfo() 的類型是 (Continuation)-> Object,接收 Continuation 為參數(shù),返回值是 Object。而在這里,函數(shù)簽名的變化可以分為兩個部分:函數(shù)簽名的變化可以分為兩個部分:函數(shù)參數(shù)的變化和函數(shù)返回值的變化。
1.CPS 參數(shù)變化
suspend() 變成 (Continuation)
suspend fun getUserInfoContent(): String {
withContext(Dispatchers.IO) {
delay(1000L)
}
return "UserInfo"
}
suspend fun getFriendListContent(user: String): String {
withContext(Dispatchers.IO) {
delay(1000L)
}
return "Friend1, Friend2"
}
suspend fun getFeedListContent(user: String, list: String): String {
withContext(Dispatchers.IO) {
delay(1000L)
}
return "{FeddList...}"
}
suspend fun fetchContent() {
val userInfoContent = getUserInfoContent()
val friendListContent = getFriendListContent(userInfoContent)
val feedListContent = getFeedListContent(userInfoContent, friendListContent)
}
上述代碼轉(zhuǎn)換成java代碼如下:
public final class TestCoroutionKt {
@Nullable
public static final Object getUserInfoContent(@NotNull Continuation var0) {
Object $continuation;
label20: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCoroutionKt.getUserInfoContent(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
CoroutineContext var10000 = (CoroutineContext)Dispatchers.getIO();
Function2 var10001 = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
if (DelayKt.delay(1000L, this) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
});
((<undefinedtype>)$continuation).label = 1;
if (BuildersKt.withContext(var10000, var10001, (Continuation)$continuation) == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "UserInfo";
}
@Nullable
public static final Object getFriendListContent(@NotNull String var0, @NotNull Continuation var1) {
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCoroutionKt.getFriendListContent((String)null, this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
CoroutineContext var10000 = (CoroutineContext)Dispatchers.getIO();
Function2 var10001 = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
if (DelayKt.delay(1000L, this) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
});
((<undefinedtype>)$continuation).label = 1;
if (BuildersKt.withContext(var10000, var10001, (Continuation)$continuation) == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "Friend1, Friend2";
}
@Nullable
public static final Object getFeedListContent(@NotNull String var0, @NotNull String var1, @NotNull Continuation var2) {
Object $continuation;
label20: {
if (var2 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var2;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var2) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCoroutionKt.getFeedListContent((String)null, (String)null, this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
CoroutineContext var10000 = (CoroutineContext)Dispatchers.getIO();
Function2 var10001 = (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
if (DelayKt.delay(1000L, this) == var2) {
return var2;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
});
((<undefinedtype>)$continuation).label = 1;
if (BuildersKt.withContext(var10000, var10001, (Continuation)$continuation) == var5) {
return var5;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "{FeddList...}";
}
@Nullable
public static final Object fetchContent(@NotNull Continuation var0) {
Object $continuation;
label37: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label37;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
Object L$0;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCoroutionKt.fetchContent(this);
}
};
}
Object var10000;
label31: {
String userInfoContent;
Object var6;
label30: {
Object $result = ((<undefinedtype>)$continuation).result;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
var10000 = getUserInfoContent((Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
case 2:
userInfoContent = (String)((<undefinedtype>)$continuation).L$0;
ResultKt.throwOnFailure($result);
var10000 = $result;
break label30;
case 3:
ResultKt.throwOnFailure($result);
var10000 = $result;
break label31;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
userInfoContent = (String)var10000;
((<undefinedtype>)$continuation).L$0 = userInfoContent;
((<undefinedtype>)$continuation).label = 2;
var10000 = getFriendListContent(userInfoContent, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
String friendListContent = (String)var10000;
((<undefinedtype>)$continuation).L$0 = null;
((<undefinedtype>)$continuation).label = 3;
var10000 = getFeedListContent(userInfoContent, friendListContent, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
String var3 = (String)var10000;
return Unit.INSTANCE;
}
}
每一次函數(shù)調(diào)用的時候,continuation 都會作為最后一個參數(shù)傳到掛起函數(shù)里,Kotlin 編譯器幫我們做的,我們開發(fā)者是無感知。
2.CPS 返回值變化
final Object getUserInfoContent(@NotNull Continuation var0)
final Object getFriendListContent(@NotNull String var0, @NotNull Continuation var1)
final Object getFeedListContent(@NotNull String var0, @NotNull String var1, @NotNull Continuation var2)
suspend fun getUserInfoContent(): String {}
fun getUserInfoContent(cont: Continuation): Any? {}
經(jīng)過 CPS 轉(zhuǎn)換后,完整的函數(shù)簽名如下:
suspend fun getUserInfoContent(): String {}
fun getUserInfoContent(cont: Continuation<String>): Any? {}
Kotlin 編譯器的 CPS 轉(zhuǎn)換是等價的轉(zhuǎn)換。suspend () -> String 轉(zhuǎn)換成 (Continuation) -> Any?。
掛起函數(shù)經(jīng)過 CPS 轉(zhuǎn)換后,它的返回值有一個重要作用:標(biāo)志該掛起函數(shù)有沒有被掛起。
其實掛起函數(shù)也能不被掛起。
首先只要有 suspend 修飾的函數(shù),它就是掛起函數(shù)。
suspend fun getUserInfoContent(): String {
withContext(Dispatchers.IO) {
delay(1000L)
}
return "UserInfo"
}
執(zhí)行到 withContext{} 的時候,就會返回 CoroutineSingletons.COROUTINE_SUSPENDED 表示函數(shù)被掛起了。
下面的函數(shù)則是偽掛起函數(shù)
suspend fun getUserInfoContent2(): String {
return "UserInfo"
}
因為它的方法體跟普通函數(shù)一樣。它跟一般的掛起函數(shù)有個區(qū)別:在執(zhí)行的時候,它并不會被掛起,因為它就是個普通函數(shù)。
二、掛起函數(shù)的反編譯
@Nullable
public static final Object fetchContent(@NotNull Continuation var0) {
Object $continuation;
label37: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label37;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
Object L$0;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCoroutionKt.fetchContent(this);
}
};
}
Object var10000;
label31: {
String userInfoContent;
Object var6;
label30: {
Object $result = ((<undefinedtype>)$continuation).result;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
var10000 = getUserInfoContent((Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
case 2:
userInfoContent = (String)((<undefinedtype>)$continuation).L$0;
ResultKt.throwOnFailure($result);
var10000 = $result;
break label30;
case 3:
ResultKt.throwOnFailure($result);
var10000 = $result;
break label31;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
userInfoContent = (String)var10000;
((<undefinedtype>)$continuation).L$0 = userInfoContent;
((<undefinedtype>)$continuation).label = 2;
var10000 = getFriendListContent(userInfoContent, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
String friendListContent = (String)var10000;
((<undefinedtype>)$continuation).L$0 = null;
((<undefinedtype>)$continuation).label = 3;
var10000 = getFeedListContent(userInfoContent, friendListContent, (Continuation)$continuation);
if (var10000 == var6) {
return var6;
}
}
String var3 = (String)var10000;
return Unit.INSTANCE;
}
label 是用來代表協(xié)程狀態(tài)機(jī)當(dāng)中狀態(tài);
result 是用來存儲當(dāng)前掛起函數(shù)執(zhí)行結(jié)果;
invokeSuspend 這個函數(shù),是整個狀態(tài)機(jī)的入口,它會將執(zhí)行流程轉(zhuǎn)交給 fetchContent 進(jìn)行再次調(diào)用;
userInfoContent, friendListContent用來存儲歷史掛起函數(shù)執(zhí)行結(jié)果。
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label37;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
Object L$0;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return TestCoroutionKt.fetchContent(this);
}
};
invokeSuspend 最終會調(diào)用 fetchContent;
如果是初次運(yùn)行,會創(chuàng)建一個 ContinuationImpl對象,completion 作為參數(shù);這相當(dāng)于用一個新的 Continuation 包裝了舊的 Continuation;
如果不是初次運(yùn)行,直接將 completion 賦值給 continuation;這說明 continuation 在整個運(yùn)行期間,只會產(chǎn)生一個實例,這能極大地節(jié)省內(nèi)存開銷(對比 CallBack)。
// result 接收協(xié)程的運(yùn)行結(jié)果
var result = continuation.result
// suspendReturn 接收掛起函數(shù)的返回值
var suspendReturn: Any? = null
// CoroutineSingletons 是個枚舉類
// COROUTINE_SUSPENDED 代表當(dāng)前函數(shù)被掛起了
val sFlag = CoroutineSingletons.COROUTINE_SUSPENDED
continuation.label 是狀態(tài)流轉(zhuǎn)的關(guān)鍵,continuation.label 改變一次,就代表了掛起函數(shù)被調(diào)用了一次;每次掛起函數(shù)執(zhí)行完后,都會檢查是否發(fā)生異常;
fetchContent 里的原本的代碼,被拆分到狀態(tài)機(jī)里各個狀態(tài)中,分開執(zhí)行;getUserInfoContent(continuation)、getFriendListContent(user, continuation)、getFeedListContent(friendList, continuation) 三個函數(shù)調(diào)用的是同一個 continuation 實例;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
如果一個函數(shù)被掛起了,它的返回值會是 CoroutineSingletons.COROUTINE_SUSPENDED;
在掛起函數(shù)執(zhí)行的過程中,狀態(tài)機(jī)會把之前的結(jié)果以成員變量的方式保存在 continuation 中。
本質(zhì)上來說,Kotlin 協(xié)程就是通過 label 代碼段嵌套,配合 switch 巧妙構(gòu)造出一個狀態(tài)機(jī)結(jié)構(gòu)。
三、Continuation
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
@Suppress("WRONG_MODIFIER_TARGET")
public suspend inline val coroutineContext: CoroutineContext
get() {
throw NotImplementedError("Implemented as intrinsic")
}
注意上面的suspend inline val coroutineContext,suspend 的這種用法只是一種特殊用法。它的作用:它是一個只有在掛起函數(shù)作用域下,才能訪問的頂層的不可變的變量。這里的 inline,意味著它的具體實現(xiàn)會被直接復(fù)制到代碼的調(diào)用處。
suspend fun testContext() = coroutineContext
@Nullable
public static final Object testContext(@NotNull Continuation $completion) {
return $completion.getContext();
}
“suspend inline val coroutineContext”,本質(zhì)上就是 Kotlin 官方提供的一種方便開發(fā)者在掛起函數(shù)當(dāng)中,獲取協(xié)程上下文的手段。它的具體實現(xiàn),其實是 Kotlin 編譯器來完成的。
我們在掛起函數(shù)當(dāng)中無法直接訪問 Continuation 對象,但可以訪問到 Continuation 當(dāng)中的 coroutineContext。要知道,正常情況下,我們想要訪問 Continuation.coroutineContext,首先是要拿到 Continuation 對象的。但是,Kotlin 官方通過“suspend inline val coroutineContext”這個頂層變量,讓我們開發(fā)者能直接拿到 coroutineContext,卻對 Continuation 毫無感知。
掛起函數(shù)與 CoroutineContext 確實有著緊密的聯(lián)系。每個掛起函數(shù)當(dāng)中都會有 Continuation,而每個 Continuation 當(dāng)中都會有 coroutineContext。并且,我們在掛起函數(shù)當(dāng)中,就可以直接訪問當(dāng)前的 coroutineContext。
原文鏈接:https://blog.csdn.net/zhangying1994/article/details/127707821
相關(guān)推薦
- 2022-02-28 Module not found: Error: Can't resolve 'sass-loade
- 2022-06-27 教你用Python按順序讀取文件夾中文件_python
- 2022-10-10 react通過組件拆分實現(xiàn)購物車界面詳解_React
- 2022-08-28 go?zero微服務(wù)實戰(zhàn)處理每秒上萬次的下單請求_Golang
- 2024-01-13 LocalDate、LocalDateTime與timestamp、Date的轉(zhuǎn)換
- 2022-06-08 FreeRTOS實時操作系統(tǒng)在Cortex-M3上的移植過程_操作系統(tǒng)
- 2022-12-27 go?build失敗報方法undefined的解決過程_Golang
- 2022-05-14 解決Linux未啟用網(wǎng)卡的問題_Linux
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支