成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

為什么我放棄使用 Kotlin 中的協(xié)程?

開發(fā) 前端
實(shí)不相瞞,我對(duì) Kotlin 這門編程語言非常喜歡,盡管它有一些缺點(diǎn)和奇怪的設(shè)計(jì)選擇。我曾經(jīng)參與過一個(gè)使用 Kotlin、Kotlin 協(xié)程(coroutine, 下同)和基于協(xié)程的服務(wù)器框架 KTOR 的中型項(xiàng)目。

 實(shí)不相瞞,我對(duì) Kotlin 這門編程語言非常喜歡,盡管它有一些缺點(diǎn)和奇怪的設(shè)計(jì)選擇。我曾經(jīng)參與過一個(gè)使用 Kotlin、Kotlin 協(xié)程(coroutine, 下同)和基于協(xié)程的服務(wù)器框架 KTOR 的中型項(xiàng)目。這個(gè)技術(shù)組合提供了很多優(yōu)點(diǎn),但是我也發(fā)現(xiàn),與常規(guī)的 Spring Boot 相比,它們很難使用。

[[330673]]

聲明:我無意抨擊相關(guān)技術(shù),我的目的僅是分享我的使用體驗(yàn),并解釋為什么我以后不再考慮使用。

調(diào)試

請(qǐng)看下面一段代碼。

  1. suspend fun retrieveData(): SomeData { 
  2.     val request = createRequest() 
  3.     val response = remoteCall(request) 
  4.     return postProcess(response) 
  5.  
  6. private suspend fun remoteCall(request: Request): Response { 
  7.    // do suspending REST call 

假設(shè)我們要調(diào)試 retrieveData 函數(shù),可以在第一行中放置一個(gè)斷點(diǎn)。然后啟動(dòng)調(diào)試器(我使用的是 IntelliJ),它在斷點(diǎn)處停止。現(xiàn)在我們執(zhí)行一個(gè) Step Over(跳過調(diào)用 createRequest),這也正常。但是如果再次 Step Over,程序就會(huì)直接運(yùn)行,調(diào)用 remoteCall() 之后不會(huì)停止。

為什么會(huì)這樣?JVM 調(diào)試器被綁定到一個(gè) Thread 對(duì)象上。當(dāng)然,這是一個(gè)非常合理的選擇。然而,當(dāng)引入?yún)f(xié)程之后,一個(gè)線程不再做一件事。仔細(xì)一看:remoteCall(request) 調(diào)用的是一個(gè) suspend 函數(shù),雖然我們?cè)谡{(diào)用它的時(shí)候并沒有在語法中看到它。那么會(huì)發(fā)生什么?我們執(zhí)行調(diào)試器 "step over ",調(diào)試器運(yùn)行 remoteCall 的代碼并等待。

這就是難點(diǎn)所在:當(dāng)前線程(我們的調(diào)試器被綁定到該線程)只是我們的coroutine 的執(zhí)行者。當(dāng)我們調(diào)用 suspend 函數(shù)時(shí),會(huì)發(fā)生的情況是,在某個(gè)時(shí)刻,suspend 函數(shù)會(huì) yield。這意味著另外一個(gè) Thread 將繼續(xù)執(zhí)行我們的方法。我們有效地欺騙了調(diào)試器。

我發(fā)現(xiàn)的唯一的解決方法是在我想執(zhí)行的行上放置一個(gè)斷點(diǎn),而不是使用Step Over。不用說,這是個(gè)大麻煩。而且很顯然,這不僅僅是我一個(gè)人的問題。

此外,在一般的調(diào)試中,很難確定一個(gè)單一的 coroutine 當(dāng)前在做什么,因?yàn)樗诰€程之間跳躍。當(dāng)然,coroutine 是有名字的,你可以在日志中不僅打印線程,還可以打印 coroutine 的名字,但根據(jù)我的經(jīng)驗(yàn),調(diào)試基于 coroutine 的代碼所需的心智負(fù)擔(dān),要比基于線程的代碼高很多。

REST 調(diào)用中綁定 context 數(shù)據(jù)

在微服務(wù)上開發(fā),一個(gè)常見的設(shè)計(jì)模式是,接收一個(gè)某種形式認(rèn)證的 REST 調(diào)用,并將相同的認(rèn)證傳遞給其他微服務(wù)的所有內(nèi)部調(diào)用。在最簡(jiǎn)單的情況下,我們至少要保留調(diào)用者的用戶名。

然而,如果這些對(duì)其他微服務(wù)的調(diào)用在我們調(diào)用棧中嵌套了 10 層深度怎么辦?我們當(dāng)然不希望在每個(gè)函數(shù)中都傳遞一個(gè)認(rèn)證對(duì)象作為參數(shù)。我們需要某種形式的 "context",這種 context 是隱性存在的。

在傳統(tǒng)的基于線程的框架中,如 Spring,解決這個(gè)問題的方法是使用 ThreadLocal 對(duì)象。這使得我們可以將任何一種數(shù)據(jù)綁定到當(dāng)前線程。只要一個(gè)線程對(duì)應(yīng)一個(gè) REST 調(diào)用(你應(yīng)該始終以這個(gè)為目標(biāo)),這正是我們需要的。這個(gè)模式的很好的例子是 Spring 的 SecurityContextHolder。

對(duì)于 coroutine,情況就不同了。一個(gè) ThreadLocal 不再對(duì)應(yīng)一個(gè)協(xié)程,因?yàn)槟愕墓ぷ髫?fù)載會(huì)從一個(gè)線程跳到另一個(gè)線程;不再是一個(gè)線程在其整個(gè)生命周期內(nèi)伴隨一個(gè)請(qǐng)求。在 Kotlin coroutine 中,有 CoroutineContext。本質(zhì)上,它不過是一個(gè) HashMap,與 coroutine 一起攜帶(無論它運(yùn)行在哪個(gè)線程上)。它有一個(gè)可怕的過度設(shè)計(jì)的 API,使用起來很麻煩,但這不是這里的主要問題。

真正的問題是,coroutine 不會(huì)自動(dòng)繼承上下文。

例如:

  1. suspend fun sum(): Int { 
  2.     val jobs = mutableListOf<Deferred<Int>>() 
  3.     for(child in children){ 
  4.         jobs += async {  // we lose our context here! 
  5.             child.evaluate()  
  6.         } 
  7.     } 
  8.     return jobs.awaitAll().sum() 

每當(dāng)你調(diào)用一個(gè) coroutine builder,如 async、runBlocking 或 launch,你將(默認(rèn)情況下)失去你當(dāng)前的 coroutine 上下文。你可以通過將上下文顯式地傳遞到 builder 方法中來避免這種情況,但是上帝保佑你不要忘記這樣做(編譯器不會(huì)管這些!)。

一個(gè)子 coroutine 可以從一個(gè)空的上下文開始,如果有一個(gè)上下文元素的請(qǐng)求進(jìn)來,但沒有找到任何東西,可以向父 coroutine 上下文請(qǐng)求該元素。然而,在 Kotlin 中不會(huì)發(fā)生這種情況,開發(fā)人員需要手動(dòng)完成,每一次都是如此。

如果你對(duì)這個(gè)問題的細(xì)節(jié)感興趣,我建議你看看這篇博文。

https://blog.tpersson.io/2018/04/22/emulating-request-scoped-objects-with-kotlin-coroutines/

synchronized 不再如你想的那樣工作

在 Java 中處理鎖或 synchronized 同步塊時(shí),我考慮的語義通常是 "當(dāng)我在這個(gè)塊中執(zhí)行時(shí),其他調(diào)用不能進(jìn)入"。當(dāng)然“其他調(diào)用”意味著存在某種身份,在這里就是線程,這應(yīng)該在你的腦海中升起一個(gè)大紅色的警告信號(hào)。

看看下面的例子。

  1. val lock = ReentrantLock() 
  2.  
  3. suspend fun doWithLock(){ 
  4.    lock.withLock { 
  5.        callSuspendingFunction() 
  6.    } 

這個(gè)調(diào)用很危險(xiǎn),即使 callSuspendingFunction() 沒有做任何危險(xiǎn)的操作,代碼也不會(huì)像你想象的那樣工作。

  • 進(jìn)入同步鎖
  • 調(diào)用 suspend 功能
  • 協(xié)程 yield,當(dāng)前線程仍然持有鎖。
  • 另一個(gè)線程繼續(xù)我們的 coroutine
  • 還是同一個(gè)協(xié)程,但我們不再是鎖的 owner 了!

潛在的沖突、死鎖或其他不安全的情況數(shù)量驚人。你可能會(huì)說,我們只是需要設(shè)計(jì)我們的代碼來處理這個(gè)問題。我同意,然而我們談?wù)摰氖?JVM。那里有一個(gè)龐大的 Java 庫(kù)生態(tài)。而它們并沒有做好處理這些情況的準(zhǔn)備。

這里的結(jié)果是:當(dāng)你開始使用 coroutine 的時(shí)候,你就放棄了使用很多 Java 庫(kù)的可能性,因?yàn)樗鼈兡壳爸荒芄ぷ髟诨诰€程的環(huán)境。

單機(jī)吞吐量與水平擴(kuò)展

對(duì)于服務(wù)器端來說,coroutine 的一大優(yōu)勢(shì)是,一個(gè)線程可以處理更多的請(qǐng)求;當(dāng)一個(gè)請(qǐng)求等待數(shù)據(jù)庫(kù)響應(yīng)時(shí),同一個(gè)線程可以愉快地服務(wù)另一個(gè)請(qǐng)求。特別是對(duì)于 I/O 密集型任務(wù),這可以提高吞吐量。

然而,正如這篇博文所希望向您展示的那樣,在許多層面上,使用 coroutine 都有一個(gè)非零成本的開銷。

由此產(chǎn)生的問題是:這個(gè)收益是否值得這個(gè)成本?而在我看來,答案是否定的。在云和微服務(wù)環(huán)境中,有一些現(xiàn)成的擴(kuò)展機(jī)制,無論是 Google AppEngine、AWS Beanstalk 還是某種形式的 Kubernetes。如果當(dāng)前負(fù)載增加,這些技術(shù)將簡(jiǎn)單地按需生成你的微服務(wù)的新實(shí)例。因此,考慮到引入 coroutine 帶來的額外成本,單一實(shí)例所能處理的吞吐量就不那么重要了。這就降低了我們使用 coroutine 所獲得的價(jià)值。

Coroutine 有其存在的價(jià)值

話說回來,Coroutine 還是有其使用場(chǎng)景。當(dāng)開發(fā)只有一個(gè) UI 線程的客戶端 UI 時(shí),coroutine 可以幫助改善你的代碼結(jié)構(gòu),同時(shí)符合 UI 框架的要求。聽說這個(gè)在安卓系統(tǒng)上很好用。Coroutine 是一個(gè)有趣的主題,然而對(duì)于服務(wù)器端開發(fā)來說,我覺得協(xié)程還差點(diǎn)意思。JVM 開發(fā)團(tuán)隊(duì)目前正在開發(fā) Fiber,本質(zhì)上也是 coroutine,但他們的目標(biāo)是與 JVM 基礎(chǔ)庫(kù)更好共存。這將是有趣的,看它將來如何發(fā)展,以及 Jetbrains 對(duì) Kotlin coroutine 對(duì)此會(huì)有什么反應(yīng)。在最好的情況下,Kotlin coroutine 將來只是簡(jiǎn)單映射到 Fiber 上,而調(diào)試器也能足夠聰明來正確處理它們。

英文原文:

https://dev.to/martinhaeusler/why-i-stopped-using-coroutines-in-kotlin-kg0

本文轉(zhuǎn)載自微信公眾號(hào)「高可用架構(gòu)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系高可用架構(gòu)公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 高可用架構(gòu)
相關(guān)推薦

2023-10-24 19:37:34

協(xié)程Java

2023-07-23 17:19:34

人工智能系統(tǒng)

2019-10-23 14:34:15

KotlinAndroid協(xié)程

2020-08-14 10:40:35

RestTemplatRetrofitJava

2021-02-01 07:20:51

KafkaPulsar搜索

2020-03-03 15:31:47

ReactVue前端

2021-05-20 09:14:09

Kotlin協(xié)程掛起和恢復(fù)

2022-10-28 10:45:22

Go協(xié)程GoFrame

2011-06-08 10:30:08

MongoDB

2018-12-21 11:26:49

MySQLMongoDB數(shù)據(jù)庫(kù)

2020-02-19 14:16:23

kotlin協(xié)程代碼

2025-05-16 08:21:45

2020-07-23 08:07:47

數(shù)組upData庫(kù)函數(shù)

2021-09-16 09:59:13

PythonJavaScript代碼

2021-04-28 09:08:23

Kotlin協(xié)程代碼

2025-02-28 09:04:08

2023-11-17 11:36:59

協(xié)程纖程操作系統(tǒng)

2021-04-25 09:36:20

Go協(xié)程線程

2017-10-23 12:42:42

2019-09-17 15:30:13

Java編程語言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 999www视频免费观看 | 久久国产精品免费一区二区三区 | 6996成人影院网在线播放 | 偷拍自拍网 | 中文字幕成人 | 亚洲视频在线观看 | 国产在线永久免费 | 日韩欧美一级精品久久 | 一级黄色毛片免费 | 国产日韩精品在线 | 一区二区国产精品 | 久久久久久九九九九九九 | 9999国产精品欧美久久久久久 | 午夜精品久久久久久久久久久久久 | 成年网站在线观看 | 九九激情视频 | 国产精品中文字幕在线观看 | 久久中文字幕一区 | 国产成人精品久久 | 国产精品爱久久久久久久 | 欧美成年视频 | 久草青青草 | 久久国产视频播放 | 欧美久久一区二区 | 97国产成人 | 日本久久久久久久久 | 日韩视频一区二区三区 | 在线日韩中文字幕 | 日韩一区三区 | 天天夜夜操 | 精品综合久久 | 日朝毛片 | 日韩快播电影网 | 久久av一区二区三区 | 国产视频一区二区在线观看 | 欧美8一10sex性hd | 99re视频在线免费观看 | 日韩在线一区二区三区 | 中国毛片免费 | 欧洲高清转码区一二区 | 久久久国产精品 |