Python 升級之路 ( Lv28 ) 并發編程初識
今天我們將學習并發編程涉及的幾個概念,包括CPU執行任務的三種方式:串行、并行、并發;程序的三種存在形式:進程、線程、協程以及相關衍生概念;再加上消息通信的兩種方式: 同步、異步。

今日冒險片段上:
傳說暗黑城的盡頭每次都會自動改變路徑,許多前輩進入這里再也沒有出來,暗影迷宮因此得名。了不起從蜘蛛洞穴深入到迷宮后,便發覺這里到處充滿了陰森和恐懼。由于長期生活在陰暗潮濕的環境中,精靈們發生了變異,這些類似于人形的上位精靈不但各種屬性增強了許多,就連智商也在逐漸與人類接近。??
眼看著就要突破迷宮的出口,三道黑影突然閃現在勇士的面前,嘴里還嘟囔著:“就憑你們也想活著走出這里?先過了我這關再說吧。”說話的這位就是暗影迷宮的領主——影子劍士剎影,邊上兩位毫無疑問就是這里的將領了。身為阿拉德大陸的冒險家們自然不會因為這句話就膽怯,一場戰斗一觸即發,俗話說的好“明槍易躲,暗箭難防!”這個強悍的影子劍士居然會鬼劍士的所有技能,什么鬼影步、鬼斬、冰霜薩亞,十分棘手,更可恨是,那兩位將領狩魂者和莫比一個不停的在遠處釋放回旋鏢,一個在暗處安放地雷,著實令人頭疼。
一、串行 & 并行 & 并發
1. 串行
一句話概: 一個CPU上,按順序完成多個任務
串行圖解:

2. 并發
一句話概括: 一個cpu上, 交替執行多個任務
并發圖解:

3. 并行
一句話概括: 多個cpu上, 同時執行多個任務(任務數<=CPU核數)并行必須有多cpu才能實現,否則只能實現并發(偽并行)
并行圖解:

串行, 并行, 并發總結如下:
- 串行: 一個CPU上,按順序完成多個任務
- 并行: 多個cpu上, 同時執行多個任務(任務數<=CPU核數)
- 并發: 一個cpu上, 交替執行多個任務
二、進程 & 線程 & 協程
1. 進程
進程是資源(CPU、內存等)分配的基本單位,它是程序執行時的一個實例.程序運行時系統就會創建一個進程,并為它分配資源,然后把該進程放入進程就緒隊列,進程調度器選中它的時候就會為它分配CPU時間,程序開始真正運行.
進程的優點:
- 可以使用計算機多核,進行任務的并行執行,提高執行效率
- 運行不受其他進程影響,創建方便
- 空間獨立,數據安全
進程的缺點:
- 進程的創建和刪除消耗的系統資源較多
2. 線程
線程是程序執行時的最小單位,也是CPU調度和分派的基本單位.一個進程可以由很多個線程組成,擁有自己獨立的棧和共享的堆,共享堆,不共享棧,標準線程由操作系統調度. 線程由CPU獨立調度執行,在多CPU環境下就允許多個線程同時運行. 同樣多線程也可以實現并發操作,每個請求分配一個線程來處理.
多線程多線程可以理解為在同一個程序中能夠同時運行多個不同的線程來執行不同的任務,這些線程可以同時利用CPU的多個核心運行. 多線程編程能夠最大限度的利用CPU的資源: 如果某些線程不需要占用CPU時間片時, 可以讓出當前時間片, 讓其他線程獲取到CPU資源. 以此來達到最大限度利用CPU資源的目的. 這個過程也被成為上下文切換。
線程生命周期線程主要有“**新建”(NEW)、“就緒”(RUNNABLE)、“運行”(RUNNING)、“阻塞”(BLOCKED)、“死亡”(DEAD)**** 五種狀態. 各狀態間的轉換如下圖所示:

注意事項:
- 在運行過程中,線程由就緒態(RUNNABLE )轉為非就緒態(BLOCKED )的過程就是線程上下文切換
- 線程的狀態由運行轉為阻塞 ,再由阻塞轉為就緒 ,然后再被調度器選中執行,這就是一個上下文切換的過程
- 當一個線程從運行狀態轉為阻塞狀態時,我們稱為一個線程的暫停,線程暫停被切出之后,操作系統會保存相應的上下文,以便這個線程稍后再次進入就緒狀態時能夠在之前執行進度的基礎上繼續執行
- 當一個線程從阻塞狀態進入到就緒狀態時,我們稱為一個線程的喚醒,此時線程將獲取上次保存的上下文繼續完成執行
- 頻繁的上下文切換會帶來系統開銷, 因此會導致系統性能下降. 所以我們在使用多線程是應該盡量避免出現上下文切換.
3. 協程
協程也叫作纖程(Fiber),是一種在線程中,比線程更加輕量級的存在,由程序員自己寫程序來管理.我們可以將協程理解為運行在線程上的代碼塊, 協程掛起并不會引起線程阻塞, 他的作用是提高線程的利用率.. 協程之間可以依靠郵箱來進行通信和數據共享, 了避免內存共享數據而帶來的線程安全問題. 因為其輕量和高利用率的特點, 即使創建上千個線程也不會對系統造成很大負擔, 而線程則恰恰相反. 協程是一種設計思想,不僅僅局限于某一門語言. 在Go, Java, Python 等語言中均有實現。
協程的核心(控制流的讓出和恢復):
- 每個協程有自己的執行棧,可以保存自己的執行現場
- 可以由用戶程序按需創建協程(比如:遇到io操作)
- 協程“主動讓出(yield)”執行權時候,會保存執行現場(保存中斷時的寄存器上下文和棧),然后切換到其他協程
- 協程恢復執行(resume)時,根據之前保存的執行現場恢復到中斷前的狀態,繼續執行,這樣就通過協程實現了輕量的由用戶態調度的多任務模
進程和線程之間的區別:
- 每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷.
- 線程可以看成是輕量級的進程,屬于同一進程的線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換的開銷小.
- 線程和進程最根本的區別在于:進程是資源分配的單位,線程是調度和執行的單位.
- 多進程: 在操作系統中能同時運行多個任務(程序).
- 多線程: 在同一應用程序中有多個順序流同時執行.
- 線程是進程的一部分,所以線程有的時候被稱為輕量級進程.
- 一個沒有線程的進程是可以被看作單線程的,如果一個進程內擁有多個線程,進程的執行過程不是一條線(線程)的,而是多條線(線程)共同完成的.
- 系統在運行的時候會為每個進程分配不同的內存區域,但是不會為線程分配內存(線程所使用的資源是它所屬的進程的資源),線程組只能共享資源.
線程, 進程, 協程之間區別如圖所示:

三、同步 & 異步
同步和異步強調的是消息通信機制。
1. 同步
同步(synchronous):A調用B,等待B返回結果后,A繼續執行.在同步的過程中, 存在一個等待的狀態. 即: 某個事情執行時需要等待另一個個事情的結果, 才能繼續向下執行。
以打電話為例:A向B打電話時, 發出電話邀請, 只有B同意接聽之后才會進行通話, 否則會一直處于等待狀態(阻塞). 這一過程稱為同步。
2. 異步
異步(asynchronous ):A調用B,A繼續執行,不等待B返回結果;B有結果了,通知A,A再做處理.
以發短信為例:A在給B發消息, 無需等待B的反饋, 便可以給C發消息. 這一過程成為異步.
今日冒險片段下:
在這千鈞一發之際, 了不起突然想到了之前奧菲利亞送的卷軸. 利用這個卷軸可以召喚精靈王伊莎貝拉的虛影, 并存在一段時間. 于是二人便利用精靈王的虛影拖住影子劍士剎影, 先將實力較弱的狩魂者和莫比擊敗, 然后在三對一的情況下, 花費接近一天的時間, 艱難的將這個劍士擊敗. 在未來的某一刻, 想到這里, 他們肯定會自豪, 因為他們擊敗的是一個能自由穿梭時空的職業, 并精通鬼劍士四系職業的第五職業——暗黑武士. 就這樣, 擊敗領主剎影之后, 了不起也順利的晉升到了lv29.





























