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

為什么我放棄使用 Kotlin 中的協程?

開發 前端
實不相瞞,我對 Kotlin 這門編程語言非常喜歡,盡管它有一些缺點和奇怪的設計選擇。我曾經參與過一個使用 Kotlin、Kotlin 協程(coroutine, 下同)和基于協程的服務器框架 KTOR 的中型項目。

 實不相瞞,我對 Kotlin 這門編程語言非常喜歡,盡管它有一些缺點和奇怪的設計選擇。我曾經參與過一個使用 Kotlin、Kotlin 協程(coroutine, 下同)和基于協程的服務器框架 KTOR 的中型項目。這個技術組合提供了很多優點,但是我也發現,與常規的 Spring Boot 相比,它們很難使用。

[[330673]]

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

調試

請看下面一段代碼。

  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 

假設我們要調試 retrieveData 函數,可以在第一行中放置一個斷點。然后啟動調試器(我使用的是 IntelliJ),它在斷點處停止。現在我們執行一個 Step Over(跳過調用 createRequest),這也正常。但是如果再次 Step Over,程序就會直接運行,調用 remoteCall() 之后不會停止。

為什么會這樣?JVM 調試器被綁定到一個 Thread 對象上。當然,這是一個非常合理的選擇。然而,當引入協程之后,一個線程不再做一件事。仔細一看:remoteCall(request) 調用的是一個 suspend 函數,雖然我們在調用它的時候并沒有在語法中看到它。那么會發生什么?我們執行調試器 "step over ",調試器運行 remoteCall 的代碼并等待。

這就是難點所在:當前線程(我們的調試器被綁定到該線程)只是我們的coroutine 的執行者。當我們調用 suspend 函數時,會發生的情況是,在某個時刻,suspend 函數會 yield。這意味著另外一個 Thread 將繼續執行我們的方法。我們有效地欺騙了調試器。

我發現的唯一的解決方法是在我想執行的行上放置一個斷點,而不是使用Step Over。不用說,這是個大麻煩。而且很顯然,這不僅僅是我一個人的問題。

此外,在一般的調試中,很難確定一個單一的 coroutine 當前在做什么,因為它在線程之間跳躍。當然,coroutine 是有名字的,你可以在日志中不僅打印線程,還可以打印 coroutine 的名字,但根據我的經驗,調試基于 coroutine 的代碼所需的心智負擔,要比基于線程的代碼高很多。

REST 調用中綁定 context 數據

在微服務上開發,一個常見的設計模式是,接收一個某種形式認證的 REST 調用,并將相同的認證傳遞給其他微服務的所有內部調用。在最簡單的情況下,我們至少要保留調用者的用戶名。

然而,如果這些對其他微服務的調用在我們調用棧中嵌套了 10 層深度怎么辦?我們當然不希望在每個函數中都傳遞一個認證對象作為參數。我們需要某種形式的 "context",這種 context 是隱性存在的。

在傳統的基于線程的框架中,如 Spring,解決這個問題的方法是使用 ThreadLocal 對象。這使得我們可以將任何一種數據綁定到當前線程。只要一個線程對應一個 REST 調用(你應該始終以這個為目標),這正是我們需要的。這個模式的很好的例子是 Spring 的 SecurityContextHolder。

對于 coroutine,情況就不同了。一個 ThreadLocal 不再對應一個協程,因為你的工作負載會從一個線程跳到另一個線程;不再是一個線程在其整個生命周期內伴隨一個請求。在 Kotlin coroutine 中,有 CoroutineContext。本質上,它不過是一個 HashMap,與 coroutine 一起攜帶(無論它運行在哪個線程上)。它有一個可怕的過度設計的 API,使用起來很麻煩,但這不是這里的主要問題。

真正的問題是,coroutine 不會自動繼承上下文。

例如:

  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() 

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

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

如果你對這個問題的細節感興趣,我建議你看看這篇博文。

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

synchronized 不再如你想的那樣工作

在 Java 中處理鎖或 synchronized 同步塊時,我考慮的語義通常是 "當我在這個塊中執行時,其他調用不能進入"。當然“其他調用”意味著存在某種身份,在這里就是線程,這應該在你的腦海中升起一個大紅色的警告信號。

看看下面的例子。

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

這個調用很危險,即使 callSuspendingFunction() 沒有做任何危險的操作,代碼也不會像你想象的那樣工作。

  • 進入同步鎖
  • 調用 suspend 功能
  • 協程 yield,當前線程仍然持有鎖。
  • 另一個線程繼續我們的 coroutine
  • 還是同一個協程,但我們不再是鎖的 owner 了!

潛在的沖突、死鎖或其他不安全的情況數量驚人。你可能會說,我們只是需要設計我們的代碼來處理這個問題。我同意,然而我們談論的是 JVM。那里有一個龐大的 Java 庫生態。而它們并沒有做好處理這些情況的準備。

這里的結果是:當你開始使用 coroutine 的時候,你就放棄了使用很多 Java 庫的可能性,因為它們目前只能工作在基于線程的環境。

單機吞吐量與水平擴展

對于服務器端來說,coroutine 的一大優勢是,一個線程可以處理更多的請求;當一個請求等待數據庫響應時,同一個線程可以愉快地服務另一個請求。特別是對于 I/O 密集型任務,這可以提高吞吐量。

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

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

Coroutine 有其存在的價值

話說回來,Coroutine 還是有其使用場景。當開發只有一個 UI 線程的客戶端 UI 時,coroutine 可以幫助改善你的代碼結構,同時符合 UI 框架的要求。聽說這個在安卓系統上很好用。Coroutine 是一個有趣的主題,然而對于服務器端開發來說,我覺得協程還差點意思。JVM 開發團隊目前正在開發 Fiber,本質上也是 coroutine,但他們的目標是與 JVM 基礎庫更好共存。這將是有趣的,看它將來如何發展,以及 Jetbrains 對 Kotlin coroutine 對此會有什么反應。在最好的情況下,Kotlin coroutine 將來只是簡單映射到 Fiber 上,而調試器也能足夠聰明來正確處理它們。

英文原文:

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

本文轉載自微信公眾號「高可用架構」,可以通過以下二維碼關注。轉載本文請聯系高可用架構公眾號。

 

責任編輯:武曉燕 來源: 高可用架構
相關推薦

2023-10-24 19:37:34

協程Java

2019-10-23 14:34:15

KotlinAndroid協程

2023-07-23 17:19:34

人工智能系統

2021-05-20 09:14:09

Kotlin協程掛起和恢復

2020-08-14 10:40:35

RestTemplatRetrofitJava

2021-02-01 07:20:51

KafkaPulsar搜索

2020-03-03 15:31:47

ReactVue前端

2022-10-28 10:45:22

Go協程GoFrame

2020-02-19 14:16:23

kotlin協程代碼

2025-05-16 08:21:45

2011-06-08 10:30:08

MongoDB

2018-12-21 11:26:49

MySQLMongoDB數據庫

2020-07-23 08:07:47

數組upData庫函數

2021-09-16 09:59:13

PythonJavaScript代碼

2021-04-28 09:08:23

Kotlin協程代碼

2025-02-28 09:04:08

2023-11-17 11:36:59

協程纖程操作系統

2021-04-25 09:36:20

Go協程線程

2017-10-23 12:42:42

2023-07-30 23:44:49

Go協程進程
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品乱码一二三区的特点 | 国产精品99999 | 手机av网| 精品二区 | 99reav | av手机在线免费观看 | 久久精品免费 | 久久精品综合 | 日本中出视频 | 国产一区二区久久 | 97caoporn国产免费人人 | 国产精品亚洲成在人线 | 国产精品成人久久久久a级 久久蜜桃av一区二区天堂 | 一级国产精品一级国产精品片 | 久久伊人免费视频 | 国产精品黄色 | 中文字幕一区二区在线观看 | 久久国产美女视频 | 国产欧美一区二区三区日本久久久 | 国产黄色av网站 | 成人欧美一区二区三区1314 | 国产高清视频在线 | 影音先锋中文字幕在线观看 | 日日摸天天添天天添破 | 一级黄色片网址 | 欧美一区中文字幕 | 欧美亚洲国产成人 | 精产国产伦理一二三区 | 国产日韩一区二区三免费 | 亚洲日本一区二区三区四区 | h在线播放 | 欧美日韩精品区 | 人人九九精 | 男人av的天堂 | 日韩在线视频一区二区三区 | 精品亚洲一区二区三区 | 国产亚洲精品精品国产亚洲综合 | 久久中文字幕在线 | 午夜男人天堂 | 日日干夜夜干 | 亚洲一区二区三区在线视频 |