天天看點

細說協程零一、協程的概念

序引:什麼是程序和線程

直白地講,程序就是應用程式的啟動執行個體。比如我們運作一個遊戲,打開一個軟體,就是開啟了一個程序。

程序擁有代碼和打開的檔案資源、資料資源、獨立的記憶體空間。

線程又是什麼呢?線程從屬于程序,是程式的實際執行者。一個程序至少包含一個主線程,也可以有多個子線程。同時,線程擁有自己的棧空間。

總結起來就是:

對作業系統來說,程序是CPU進行資源配置設定和管理的最小單元,線程是CPU排程任務執行的最小單元。

影子:什麼是協程

在解釋“什麼是協程”之前,我們先說點題外話:

細說協程零一、協程的概念

網上很多文章也用這個例子,也用這個官方例子來說明使用協程的優勢,然後就說協程是什麼輕量級的線程,又是什麼使用者态的,協程像線程但又不是線程,通過什麼禅讓機制,禅讓線程執行權,大幅度提升CPU效率,諸如此類,簡直就是胡說八道,誤人子弟。

好了,牢騷發完,我們現在回歸正題,解釋下上圖截圖的代碼情況:

官網這個例子就是通過repeat函數啟動了10000個協程,然後它讓我們試一試使用Thread來實作會發生什麼,也就是像下面這樣:

repeat(100_000) { 
    thread { 
        Thread.sleep(1000L) 
        print (".") 
    } 
}
           

這個例子我們不用跑也知道大概會發生什麼了,建立100000個線程。。。

但是,我想說的是,kotlin官方用這個例子真的有點不厚道了,用java底層的Thread類,和他們造出來的一個基于Thread類封裝的“工具包”進行對比。

真正要比的話,我們用java的Executor和他比比?

repeat(100_000) {
    val executor = Executors.newSingleThreadScheduledExecutor()
    val task = Runnable { 
        print(".") 
    } 
    repeat (100_00) { 
        executor.schedule(task, 1, TimeUnit.SECONDS) 
    }
}
           

我用上面這段程式跑了一下,用協程相對于java的線程池,并沒有發現什麼實質上的性能優勢。感興趣的也可以自己借助Android Studio Profiler試一試

是以,我們可以得出一個結論:

使用Kotlin協程,本質上其實并沒有比我們原先的開發模式有多大性能上的優勢,因為我們所使用的的OkHttp、AsyncTask等内部都幫我們封裝了線程池,而不是直接使用Thread類。

那麼Kotlin協程運作在單線程裡面嗎?

百度 Kotlin協程,找到比較高贊,閱讀量高達數萬的一篇文章,文章中截取了各種概念,到網上找了各種和協程相關的圖,但我想說,這其實是誤人子弟!

細說協程零一、協程的概念
細說協程零一、協程的概念

很簡單,我們做一個小實驗就能知道結果:

//在沒有開啟協程前,先列印一下程序名稱和程序id    
println("Main: " + "threadName = " + Thread.currentThread().name + " threadId = " + Thread.currentThread().id)
// 循環20次    
repeat(20) {
    GlobalScope.launch {
        // 開啟協程後,先列印一下程序名稱和程序id            
        println("IO: " + "threadName = " + Thread.currentThread().name + " threadId = " + Thread.currentThread().id)
        delay(1000L)
    }
}
Thread.sleep(100)
           

AndroidStudio執行結果:

細說協程零一、協程的概念

發現了什麼?所謂的協程完全就是開啟了一個新的線程來執行任務,上面我們20個任務,實際上協程隻是開啟了4個線程,然後在後面的任務中,重複的使用這4個線程,直至任務完成。這是不是跟java中的線程池邏輯幾乎完全一緻???

我們再來看看上面的代碼在IntelliJ IDEA裡面的執行結果:

細說協程零一、協程的概念

依然是為每隔新的任務建立子線程,在子線程完成任務,這跟Java的線程調用是完全一樣的。至于IntelliJ IDEA裡面的執行結果與AndroidStudio稍有不一樣,是因為kotlin語言對Android平台下的協程做了優化,以更适應Android營運環境。

看到這裡,是不是有點颠覆你原來對協程的認識?難道網上的所有文章都是錯誤的?

其實網上大部分文章說的協程,指的可能都是其他語言的協程的特點,比如Go、Lua、Python等等。而我們要學的,是Kotlin協程,它不是真正意義上的協程,它也沒有那麼的神秘,隻要你沒有魔改JVM,start了幾個線程,作業系統就會建立幾個線程,根本談不上什麼性能更好,CPU效率更高,别被忽悠到了。

好了,現在我們再來回答上面的問題——什麼是協程:

1、協程就是可以由程式自行控制挂起(暫停執行)、恢複(繼續在原來暫停的地方執行)的程式——這是核心;

2、他可以用來實作多任務的協作執行;

3、解決異步任務控制流的靈活轉移,簡化程式邏輯複雜度,看起來更簡潔舒适;

4、簡單來說協程就是讓我們能夠用同步的思維來發異步和并發程式的一個架構。**

協程的作用

1、協程可以讓異步和并發的邏輯代碼同步化;

2、可以降低異步并發程式的設計複雜度;

3、要注意的是:協程本身并不能讓我們的代碼異步,隻是讓我們的異步邏輯變得更簡單。

4、有效避免正常Android中網絡請求的地獄式回調。

var user = api.getUser()//異步的網絡請求(異步在子線程)
tvName.text = user.name//更新UI(UI主線程)
           

以及:

coroutineScope.launch(Dispatchers.Main) {       // 開始協程:主線程
    val token = api.getToken()                  // 網絡請求:IO 線程
    val user = api.getUser(token)               // 網絡請求:IO 線程
    nameTv.text = user.name                     // 更新 UI:主線程
}
           

上面getToken() 和getUser(token)兩次網絡請求,順序執行,但是需要第一個請求成功後,再利用請求結果發起第二個請求,這樣的話,不可避免的會出現:邏輯實作上的異步、請求嵌套以及請求回調,想想都頭大。但是協程很好的幫我們很好的解決了這個問題

Kotlin的協程對Thread相關的API做了一套封裝,讓我們不用過多的關心線程問題就可以友善的實作高并發和異步操作,這就是Kotlin下的“協程”。

實際上kotlin的協程最大的作用和優勢在于:你可以把運作在不同線程的異步代碼寫在同一個代碼塊裡,甚至忽略異步或者說并發這件事情的存在——異步邏輯同步思維來實作!

協程架構

細說協程零一、協程的概念

協程學習三部曲

細說協程零一、協程的概念

協程的概念:

由程式自行控制挂起(暫停執行)、恢複(繼續在原來暫停的地方執行)的程式

協程要素:

就是指kotlin協程标準函數庫裡面的各個函數

繼續閱讀