<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 參考文章 [到底什么是「非阻塞式」掛起?協程真的更輕量級嗎?](https://kaixue.io/kotlin-coroutines-3/) 在協程系列的前兩篇文章中,我們介紹了: * 協程就是個線程框架 * 協程的掛起本質就是線程切出去再切回來 ## 什么是「非阻塞式掛起」 非阻塞式是相對阻塞式而言的。不卡線程就是非阻塞式 編程語言中的很多概念其實都來源于生活,就像脫口秀的段子一樣。 線程阻塞很好理解,現實中的例子就是交通堵塞,它的核心有 3 點: * 前面有障礙物,你過不去(線程卡了) * 需要等障礙物清除后才能過去(耗時任務結束) * 除非你繞道而行(切到別的線程) 從語義上理解「非阻塞式掛起」,講的是**非阻塞式,這個是掛起的一個特點**,也就是說,**協程的掛起,就是非阻塞式的,協程是不講「阻塞式的掛起」的概念的**。 網絡上有一種說法是:協程的掛起是非阻塞式的,而線程時阻塞式的。這種說法是具有嚴重誤導性的。搞得好像協程的異步比線程的異步更高級一樣,但其實這里的線程是阻塞式的是指的是單線程是阻塞式的,因為單線程中的耗時代碼會卡線程。而單協程也可以是非阻塞式的,因為它可以利用掛起函數來切線程。但實際上Kotlin協程的掛起就是切線程而已。它和Java的切線程是完全一樣的,只是在寫法上,上下兩行連續代碼,協程可以悄悄地把線程切走再切回來,不會卡當前線程,這個就是所謂的非阻塞式掛起。而不用協程的話,上下兩行連續代碼只能是單線程的。那當然會卡線程了。所以,協程的掛起函數跟Java原始的線程切換,其實都是非阻塞式的。只是協程是看起來阻塞,但實際上卻非阻塞的寫法而已。 我們講「非阻塞式掛起」,其實它有幾個前提:并沒有限定在一個線程里說這件事,因為掛起這件事,本來就是涉及到多線程。 就像視頻里講的,阻塞不阻塞,都是針對單線程講的,一旦切了線程,肯定是非阻塞的,你都跑到別的線程了,之前的線程就自由了,可以繼續做別的事情了。 所以「非阻塞式掛起」,其實就是在講協程在掛起的同時切線程這件事情。 ## 為什么要講非阻塞式掛起 既然第三篇說的「非阻塞式掛起」和第二篇的「掛起要切線程」是同一件事情,那還有講的必要嗎? 是有的。因為它在寫法上和單線程的阻塞式是一樣的。 **協程只是在寫法上「看起來阻塞」,其實是「非阻塞」的,因為在協程里面它做了很多工作,其中有一個就是幫我們切線程**。 第二篇講掛起,重點是說切線程先切過去,然后再切回來。 **第三篇講非阻塞式,重點是說線程雖然會切,但寫法上和普通的單線程差不多**。 讓我們來看看下面的例子: ~~~kotlin main { GlobalScope.launch(Dispatchers.Main) { // ?? 耗時操作 val user = suspendingRequestUser() updateView(user) } private suspend fun suspendingRequestUser() : User = withContext(Dispatchers.IO) { api.requestUser() } } ~~~ 從上面的例子可以看到,**耗時操作和更新 UI 的邏輯像寫單線程一樣放在了一起,只是在外面包了一層協程**。 而**正是這個協程解決了原來我們單線程寫法會卡線程這件事**。 ## 阻塞的本質 首先,**所有的代碼本質上都是阻塞式的,而只有比較耗時的代碼才會導致人類可感知的等待**,比如在主線程上做一個耗時 50 ms 的操作會導致界面卡掉幾幀,這種是我們人眼能觀察出來的,而這就是我們通常意義所說的「阻塞」。 舉個例子,當你開發的 app 在性能好的手機上很流暢,在性能差的老手機上會卡頓,就是在說同一行代碼執行的時間不一樣。 視頻中講了一個網絡 IO 的例子(下面的錯誤觀點一),IO 阻塞更多是反映在「等」這件事情上,它的性能瓶頸是**和網絡的數據交換**,你切多少個線程都沒用,該花的時間一點都少不了。 而這跟協程半毛錢關系沒有,切線程解決不了的事情,協程也解決不了。 ## 協程與線程 協程我們講了 3 期,Kotlin 協程和線程是無法脫離開講的。 別的語言我不說,**在 Kotlin 里,協程就是基于線程來實現的一種更上層的工具 API,類似于 Java 自帶的 Executor 系列 API 或者 Android 的 Handler 系列 API**。 只不過呢,協程它不僅提供了方便的 API,在設計思想上是一個**基于線程的上層框架**,你可以理解為新造了一些概念用來幫助你更好地使用這些 API,僅此而已。 就像 ReactiveX 一樣,為了讓你更好地使用各種操作符 API,新造了 Observable 等概念。 說到這里,Kotlin 協程的三大疑問:協程是什么、掛起是什么、掛起的非阻塞式是怎么回事,就已經全部講完了。非常簡單: * 協程就是切線程; * 掛起就是可以自動切回來的切線程; * 掛起的非阻塞式指的是它能用看起來阻塞的代碼寫出非阻塞的操作,就這么簡單。 當然了,這幾句是總結,它們背后的原理你是一定要掌握住的。如果忘了,再去把之前的視頻和文章看一遍就好。 視頻中還糾正了官方文檔里面的一個錯誤(協程是輕量級的),這里就不再重復了,最后想表達一點: **Kotlin 協程并沒有脫離 Kotlin 或者 JVM 創造新的東西,它只是將多線程的開發變得更簡單了,可以說是因為 Kotlin 的誕生而順其自然出現的東西,從語法上看它很神奇,但從原理上講,它并不是魔術**。 ## 錯誤的網絡觀點 ### 錯誤觀點一、協程的非阻塞式比線程的更加高效,**其實這是錯誤的觀點** * 網友解釋:如果用線程來處理網絡請求,那么在網絡請求返回之前,線程會一直等著它,是處于阻塞式狀態不做事的,那么這就導致了線程的利用率不高;而如果用協程,由于協程在等待網絡請求的過程中會被掛起,線程沒有被阻塞,這就提高了線程的利用率。 * 反方(凱哥):聽著這種解釋好有道理,但這種說法是錯誤的,首先,所有的代碼本質上都是阻塞式的,而只有比較耗時的代碼才能導致人類可感知的等待,比如你在主線程做一個幾十毫秒的操作,它就會導致你的界面卡掉幾幀,這是我們肉眼可以觀察到的,而這就是我們通常意義所說的阻塞,而耗時操作,我們之前也講了,一共分為兩種,I/O 操作和 CPU 計算耗時工作,而網絡就屬于I/O 操作,它的性能瓶頸就是I/O ,也就是和網絡的數據交互,而不是CPU的計算速度。所以線程會被網絡交互所阻塞,但這個阻塞是不可避免的,你必須做這個I/O,那它比較慢怎么辦,不怎么辦,沒辦法,你只能讓線程在那里慢慢處理,這里注意,它只是在慢慢地處理,而不是單純地等待,它等待只是因為網絡傳輸的性能低于CPU的性能,但它本質上是在工作的。這種阻塞不可避免。那協程不是可以掛起么?不用傻傻等待了么,這里你忘了,協程掛起的本質就是切線程,而網絡請求時的掛起是什么?還是切線程,它是把主線程給空置出來,然后在后臺線程去做網絡交互,而不是“先切到后臺去做網絡請求,然后這個網絡請求到達那個所謂的等待階段再掛起一次。通過這種方式讓后臺的這個網絡交互線程空出來,然后這個網絡線程就能去立即做別的網絡請求,就不用傻等了”,沒有這種好事的,你把網絡交互線程給空出來,它就能立即去做下一個網絡請求了,好爽是吧,那你剛才那個正在等待的網絡請求怎么辦?它還是有另外一個線程來承載的呀,不然你覺得這個網絡交互會憑空地自己完成?這是不可能的,一定要注意,掛起的本質就是切線程,只是它在完成之后能夠自動的切回來,沒有其他神奇之處了。所以協程的非阻塞式掛起,只是用阻塞式的方式寫出了非阻塞式的代碼而已,并沒有任何相比于線程更加高效的地方。**在Kotlin里,協程就是基于線程而實現的一套更上層的工具API**。 ### 錯誤觀點二、協程是用戶態的,它的切換不需要和操作系統進行交互,因此它的切換成本比線程低。還有人說協程由于是協作式的,所以不需要線程的同步操作。 上面的觀點對于有些語言描述是對的,但是對于Kotlin來說完全是無稽之談,所以有些文章的作者其實是不懂裝懂的。一定要記住,**協程的本質還是線程**。 ### 錯誤觀點三、Kotlin官方說Kotlin協程相當于輕量級線程。 官方的示例——[協程很輕量](http://www.kotlincn.net/docs/reference/coroutines/basics.html#%E5%8D%8F%E7%A8%8B%E5%BE%88%E8%BD%BB%E9%87%8F) ``` import kotlinx.coroutines.* fun main() = runBlocking { repeat(100_000) { // 啟動大量的協程 launch { delay(1000L) print(".") } } } ``` “同時執行10萬個延時任務,用協程沒問題,但是用線程的話,多半要內存溢出,所以協程比線程要輕量級。” 這是錯誤的,這里的比較對象是Java的Thread,而協程的比較對象更接近的應該是Java的線程池API,也就是ExecutorService那幾個類。如果是和它們作比較,使用和不使用協程的性能就不相上下了。如下面的代碼所示 ~~~ fun main(args: Array<String>) { val executor = Executors.newCachedThreadPool() val task = Runnable { Thread.sleep(1000L) print(".") } repeat(100_000) { executor.execute(task) } } ~~~ 官方狡猾的地方,除了把對比對象直接定為了Thread而不是線程池API之外,還有一點是它這個的延時操作,協程的對比對象使用的是線程的`Thread.sleep()`方法,這樣的話,如果用普通的線程池和協程比較,依然會出現協程性能更高的結果,但其實協程的這個延時操作,它對應的應該是Java里面的`Executors.newSingleThreadScheduledExecutor`,而不是上面代碼中的 `Executors.newCachedThreadPool`,如下所示,那用不用協程的性能就真的徹底沒有區別了,Thread是最底層的控件,而Executor和Coroutine都是基于它所創造出來的工具包。Kotlin官方偷換了概念,把直接使用Thread說成是比協程重,搞得好像協程在性能上真的是有優勢一樣。 ~~~ fun main(args: Array<String>) { val executor = Executors.newSingleThreadScheduledExecutor() val task = Runnable { print(".") } repeat(100_000) { executor.schedule(task, 1, TimeUnit.SECONDS) } } ~~~ ![](https://img.kancloud.cn/c6/9e/c69ea4d6e770d5b32092e24aa5fd69a3_1862x860.gif)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看