網站首頁 編程語言 正文
學習了極客時間課程,記錄下學習輸出。
一、CPS轉換
掛起函數,比普通的函數多了 suspend 關鍵字。通過suspend 關鍵字,Kotlin 編譯器就會特殊對待這個函數,將其轉換成一個帶有 Callback 的函數,這里的 Callback 就是 Continuation 接口。
例、CPS 轉換:
suspend fun getUserInfo(): Any {
return "UserInfo"
}
----->
fun getUserInfo(ct:Continuation): Any? {
ct.resumeWith("UserInfo")
return Unit
}
PS 轉換過程中,函數的類型發生了變化:suspend ()->Any 變成了 (Continuation)-> Any?。這意味著,如果你在 Java 里訪問一個 Kotlin 掛起函數 getUserInfo(),會看到 getUserInfo() 的類型是 (Continuation)-> Object,接收 Continuation 為參數,返回值是 Object。而在這里,函數簽名的變化可以分為兩個部分:函數簽名的變化可以分為兩個部分:函數參數的變化和函數返回值的變化。
1.CPS 參數變化
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)
}
上述代碼轉換成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;
}
}
每一次函數調用的時候,continuation 都會作為最后一個參數傳到掛起函數里,Kotlin 編譯器幫我們做的,我們開發者是無感知。
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? {}
經過 CPS 轉換后,完整的函數簽名如下:
suspend fun getUserInfoContent(): String {}
fun getUserInfoContent(cont: Continuation<String>): Any? {}
Kotlin 編譯器的 CPS 轉換是等價的轉換。suspend () -> String 轉換成 (Continuation) -> Any?。
掛起函數經過 CPS 轉換后,它的返回值有一個重要作用:標志該掛起函數有沒有被掛起。
其實掛起函數也能不被掛起。
首先只要有 suspend 修飾的函數,它就是掛起函數。
suspend fun getUserInfoContent(): String {
withContext(Dispatchers.IO) {
delay(1000L)
}
return "UserInfo"
}
執行到 withContext{} 的時候,就會返回 CoroutineSingletons.COROUTINE_SUSPENDED 表示函數被掛起了。
下面的函數則是偽掛起函數
suspend fun getUserInfoContent2(): String {
return "UserInfo"
}
因為它的方法體跟普通函數一樣。它跟一般的掛起函數有個區別:在執行的時候,它并不會被掛起,因為它就是個普通函數。
二、掛起函數的反編譯
@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 是用來代表協程狀態機當中狀態;
result 是用來存儲當前掛起函數執行結果;
invokeSuspend 這個函數,是整個狀態機的入口,它會將執行流程轉交給 fetchContent 進行再次調用;
userInfoContent, friendListContent用來存儲歷史掛起函數執行結果。
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 最終會調用 fetchContent;
如果是初次運行,會創建一個 ContinuationImpl對象,completion 作為參數;這相當于用一個新的 Continuation 包裝了舊的 Continuation;
如果不是初次運行,直接將 completion 賦值給 continuation;這說明 continuation 在整個運行期間,只會產生一個實例,這能極大地節省內存開銷(對比 CallBack)。
// result 接收協程的運行結果
var result = continuation.result
// suspendReturn 接收掛起函數的返回值
var suspendReturn: Any? = null
// CoroutineSingletons 是個枚舉類
// COROUTINE_SUSPENDED 代表當前函數被掛起了
val sFlag = CoroutineSingletons.COROUTINE_SUSPENDED
continuation.label 是狀態流轉的關鍵,continuation.label 改變一次,就代表了掛起函數被調用了一次;每次掛起函數執行完后,都會檢查是否發生異常;
fetchContent 里的原本的代碼,被拆分到狀態機里各個狀態中,分開執行;getUserInfoContent(continuation)、getFriendListContent(user, continuation)、getFeedListContent(friendList, continuation) 三個函數調用的是同一個 continuation 實例;
var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
如果一個函數被掛起了,它的返回值會是 CoroutineSingletons.COROUTINE_SUSPENDED;
在掛起函數執行的過程中,狀態機會把之前的結果以成員變量的方式保存在 continuation 中。
本質上來說,Kotlin 協程就是通過 label 代碼段嵌套,配合 switch 巧妙構造出一個狀態機結構。
三、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 的這種用法只是一種特殊用法。它的作用:它是一個只有在掛起函數作用域下,才能訪問的頂層的不可變的變量。這里的 inline,意味著它的具體實現會被直接復制到代碼的調用處。
suspend fun testContext() = coroutineContext
@Nullable
public static final Object testContext(@NotNull Continuation $completion) {
return $completion.getContext();
}
“suspend inline val coroutineContext”,本質上就是 Kotlin 官方提供的一種方便開發者在掛起函數當中,獲取協程上下文的手段。它的具體實現,其實是 Kotlin 編譯器來完成的。
我們在掛起函數當中無法直接訪問 Continuation 對象,但可以訪問到 Continuation 當中的 coroutineContext。要知道,正常情況下,我們想要訪問 Continuation.coroutineContext,首先是要拿到 Continuation 對象的。但是,Kotlin 官方通過“suspend inline val coroutineContext”這個頂層變量,讓我們開發者能直接拿到 coroutineContext,卻對 Continuation 毫無感知。
掛起函數與 CoroutineContext 確實有著緊密的聯系。每個掛起函數當中都會有 Continuation,而每個 Continuation 當中都會有 coroutineContext。并且,我們在掛起函數當中,就可以直接訪問當前的 coroutineContext。
原文鏈接:https://blog.csdn.net/zhangying1994/article/details/127707821
相關推薦
- 2024-03-24 go 連接redis集群
- 2022-05-22 Python讀寫yaml文件_python
- 2021-12-09 Ubuntu環境安裝Anaconda3完整步驟_Linux
- 2022-10-22 react實現Modal彈窗效果_React
- 2022-12-30 react?component?changing?uncontrolled?input報錯解決_Re
- 2022-10-15 C語言const關鍵字的用法詳解_C 語言
- 2022-05-27 C++?二叉樹的實現超詳細解析_C 語言
- 2022-12-07 聊聊C語言中sizeof運算符的一個陷阱_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支