kotlin之協程(一),線程,進程,協程,協程可以替換線程嗎?

目錄

kotlin之協程(一),線程,進程,協程,協程可以替換線程嗎?
kotlin之協程(二),Kotlin協程是什么、掛起是什么、掛起的非阻塞式
kotlin之協程(三),開始創建協程,launch,withContext
kotlin之協程(四),協程的核心關鍵字suspend
kotlin之協程(五),launch 函數以及協程的取消與超時
kotlin之協程(六),協程中的 async和launch的區別以及runBlocking
kotlin之協程(七),協程中relay、yield 區別

前言

在DataStore里面有提到 DataStore是基于協程Flow實現的,那么什么是協程呢?

先來回顧一下線程和進程

在了解協程之前,我們先回歸一下進程和線程的概念

  • 進程 直白地講,進程就是應用程序的啟動實例
  • 線程 線程從屬于進程,是程序的實際執行者。一個進程至少包含一個主線程,也可以有更多的子線程。并且線程擁有自己的棧空間
線程進程.jpeg

總結起來就是:對操作系統來說,線程是最小的執行單元,進程是最小的資源管理單元

無論進程還是線程都是有操作系統統一管理的
而線程具有五種狀態,我們看一下狀態的轉化關系


線程轉化關系.png

接下來我們了解一下并發和并行

  • 并發
    并發意味著應用程序同時(同時)在一項以上的任務上取得進展。好吧,如果計算機只有一個CPU,則應用程序可能無法 在同一時間同時完成一項以上的任務,而是在應用程序中一次要處理多個任務。在開始下一項任務之前,它并沒有完全完成一項任務。而是,CPU在不同任務之間切換,直到任務完成為止。


    并發.png

即使其中只有一個線程在運行,也可以有一個并發應用程序

  • 并行
    并行是指應用程序將其任務分解為較小的子任務,這些子任務可以并行處理,例如在多個CPU上同時進行。
并行.png

為了實現真正的并行性,您的應用程序必須運行多個線程,或者至少能夠調度要在其他線程,進程,CPU,圖形卡等中執行的任務。

不得不說的進程和線程的痛點

(1)線程的調度與缺點

在同一個進程中并行運行多個線程,是對在同一臺計算機上并行運行多個進程的模擬。因此,線程也被稱為輕量級進程。與進程調度類似,CPU在線程之間快速切換,制造了線程并行運行的假象。

由于各個線程都可以訪問進程地址空間的每一個內存地址,所以一個線程可以讀、寫,甚至清除另一個線程的堆棧。也就是說,線程之間是沒有保護的。但要注意的是,每個線程都有自己的堆棧、程序計數器、寄存器等信息,這些不是共享的。

共享地址空間雖然可以方便地共享對象,但這也導致一個問題,那就是任何一個線程出錯時,進程中的所有線程會跟著一起崩潰。這也是如Nginx等強調穩定性的服務堅持使用多進程模式的原因。事實上,無論基于多進程還是多線程,都難以實現高并發,這由三個原因所致

(2)線程內存占用

一是單個線程消耗的內存過多,比如64位的Linux為每個線程的棧分配了8MB的內存,通過ulimit -s可以查看線程的默認分配的內存。單位kb

(3)線程競爭

為了解決線程申請堆內存時,互相競爭的問題。每個線程預先在這個空間內申請堆空間還預分配了64MB的內存作為堆內存池。所以,我們沒有足夠的內存去開啟幾萬個線程實現并發。

(4)線程切換耗時

線程的切換是由內核控制的,什么時候會切換線程呢?不只時間片用盡,當調用阻塞方法時,內核為了CPU 充分工作,也會切換到其他線程執行。一次上下文切換的成本在幾十納秒到幾微秒間,當線程繁忙且數量眾多時,這些切換會消耗絕大部分的CPU運算能力。

下圖以磁盤IO為例,描述了多線程中使用阻塞方法讀磁盤,2個線程間的切換方式。

線程切換耗時.png

那么,怎么才能實現高并發呢?把上圖中本來由內核實現的請求切換工作,交由用戶態的代碼來完成就可以了。

協程

什么是協程

協程 - 也叫微線程,是一種新的多任務并發的操作手段

這中說話不夠詳細,我們換種說話

協程就是用戶態的線程。通常創建協程時,會從進程的堆中分配一段內存作為協程的棧。線程的棧有8MB,而協程棧的大小通常只有幾十 KB,更低的內存占用空間為高并發提供了保證

  • 下面是關于協程這個概念的一些描述:

協程的開發人員 Roman Elizarov 是這樣描述協程的:協程就像非常輕量級的線程。線程是由系統調度> 的,線程切換或線程阻塞的開銷都比較大。而協程依賴于線程,但是協程掛起時不需要阻塞線程,幾乎>是無代價的,協程是由開發者控制的。所以協程也像用戶態的線程,非常輕量級,一個線程中可以創建>任意個協程。

對于多線程應用,CPU通過切片的方式來切換線程間的執行,線程切換時需要耗時(保存狀態,下次繼續)。協程,則只使用一個線程,在一個線程中規定某個代碼塊執行順序。協程能保留上一次調用時的狀態,不需要像線程一樣用回調函數,所以性能上會有提升。缺點是本質是個單線程,不能利用到單個CPU的多個核

協程的調度

語言的運行時系統會幫助我們自動地創建和銷毀系統級的線程。這里的系統級線程指的就是我們剛剛說過的操作系統提供的線程。

PHP協程的問題

為了保證所有切換都在用戶態進行,協程必須重新封裝所有的阻塞系統調用,否則,一旦協程觸發了線程切換,會導致這個線程進入休眠狀態,進而其上的所有協程都得不到執行。所以,協程的高性能,建立在切換必須由用戶態代碼完成之上,這要求協程生態是完整的,要盡量覆蓋常見的組件

為什么要搞出和用協程呢

  • 是節省CPU,避免系統內核級的線程頻繁切換,造成的CPU資源浪費。好鋼用在刀刃上。而協程是用戶態的線程,用戶可以自行控制協程的創建于銷毀,極大程度避免了系統級線程上下文切換造成的資源浪費。

  • 是節約內存,在64位的Linux中,一個線程需要分配8MB棧內存和64MB堆內存,系統內存的制約導致我們無法開啟更多線程實現高并發。而在協程編程模式下,可以輕松有十幾萬協程,這是線程無法比擬的。

  • 是穩定性,前面提到線程之間通過內存來共享數據,這也導致了一個問題,任何一個線程出錯時,進程中的所有線程都會跟著一起崩潰。

  • 是開發效率,使用協程在開發程序之中,可以很方便的將一些耗時的IO操作異步化,例如寫文件、耗時IO請求等。

對于協程的一個總結

  • 特征:協程是運行在單線程中的并發程序
  • 優點:省去了傳統 Thread 多線程并發機制中切換線程時帶來的線程上下文切換、線程狀態切換、Thread 初始化上的性能損耗,能大幅度唐提高并發性能
  • 簡單理解:在單線程上由程序員自己調度運行的并行計算

寫到最后

協程本身不是替換線程的,因為協程是建立在線程之上的,但是協程能夠更好的為我們提供執行高并發任務
但是kotlin的協程的概念是否真的是這樣的呢?

(每天學習一點點.每天進步一點點,分享不宜路過點個贊呀,喜歡的點個關注后續更新不斷).

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。